@prisma/cli 3.0.0-beta.2 → 3.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -16
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +26 -7
- package/dist/adapters/mock-api.js +81 -2
- package/dist/adapters/token-storage.js +344 -25
- package/dist/cli.js +18 -3
- package/dist/cli2.js +12 -4
- package/dist/commands/agent/index.js +60 -0
- package/dist/commands/app/index.js +90 -61
- package/dist/commands/auth/index.js +55 -2
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/build/index.js +29 -0
- package/dist/commands/database/index.js +159 -0
- package/dist/commands/env.js +8 -4
- package/dist/commands/git/index.js +1 -1
- package/dist/commands/project/index.js +1 -1
- package/dist/controllers/agent.js +228 -0
- package/dist/controllers/app-env-api.js +54 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +286 -131
- package/dist/controllers/app.js +849 -309
- package/dist/controllers/auth.js +255 -11
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/build.js +88 -0
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +156 -87
- package/dist/controllers/select-prompt-port.js +1 -0
- package/dist/lib/agent/cli-command.js +17 -0
- package/dist/lib/agent/constants.js +12 -0
- package/dist/lib/agent/package-manager.js +99 -0
- package/dist/lib/agent/setup-status.js +83 -0
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +220 -104
- package/dist/lib/app/branch-database-api.js +102 -0
- package/dist/lib/app/branch-database-deploy.js +326 -0
- package/dist/lib/app/branch-database.js +216 -0
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +82 -0
- package/dist/lib/app/bun-project.js +15 -9
- package/dist/lib/app/compute-config.js +145 -0
- package/dist/lib/app/deploy-plan.js +59 -0
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
- package/dist/lib/app/env-config.js +1 -1
- package/dist/lib/app/env-file.js +82 -0
- package/dist/lib/app/env-vars.js +28 -2
- package/dist/lib/app/local-dev.js +8 -49
- package/dist/lib/app/production-deploy-gate.js +162 -0
- package/dist/lib/app/read-branch.js +30 -0
- package/dist/lib/auth/auth-ops.js +38 -23
- package/dist/lib/auth/guard.js +6 -2
- package/dist/lib/auth/login.js +117 -15
- package/dist/lib/database/provider.js +167 -0
- package/dist/lib/diagnostics.js +15 -0
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +53 -0
- package/dist/lib/git/local-status.js +57 -0
- package/dist/lib/project/interactive-setup.js +2 -1
- package/dist/lib/project/local-pin.js +183 -34
- package/dist/lib/project/resolution.js +206 -50
- package/dist/lib/project/setup.js +64 -18
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/agent.js +74 -0
- package/dist/presenters/app-env.js +149 -14
- package/dist/presenters/app.js +187 -26
- package/dist/presenters/auth.js +99 -2
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +44 -26
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/cli-command.js +12 -0
- package/dist/shell/command-arguments.js +7 -1
- package/dist/shell/command-meta.js +243 -15
- package/dist/shell/command-runner.js +60 -21
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +61 -1
- package/dist/shell/help.js +31 -20
- package/dist/shell/output.js +10 -1
- package/dist/shell/prompt.js +7 -3
- package/dist/shell/runtime.js +10 -6
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +247 -0
- package/dist/use-cases/auth.js +68 -1
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/package.json +27 -10
- package/dist/lib/app/preview-build.js +0 -284
- package/dist/lib/app/preview-interaction.js +0 -5
package/dist/controllers/app.js
CHANGED
|
@@ -1,54 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { resolvePrismaCliPackageCommand } from "../lib/agent/cli-command.js";
|
|
2
|
+
import { PRISMA_AGENT_INSTALL_ARGS, PRISMA_COMPUTE_AGENT_SKILL } from "../lib/agent/constants.js";
|
|
3
|
+
import { readPrismaAgentSetupStatus, shouldOfferPrismaAgentSetup } from "../lib/agent/setup-status.js";
|
|
4
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
3
5
|
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
+
import { runAgentInstall } from "./agent.js";
|
|
7
|
+
import { renderCommandHeader, renderSummaryLine } from "../shell/ui.js";
|
|
6
8
|
import { canPrompt } from "../shell/runtime.js";
|
|
9
|
+
import { writeJsonEvent } from "../shell/output.js";
|
|
10
|
+
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
11
|
+
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
7
12
|
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
13
|
+
import "../lib/app/app-interaction.js";
|
|
14
|
+
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
|
|
15
|
+
import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
|
|
16
|
+
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
17
|
+
import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
|
|
18
|
+
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
19
|
+
import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
20
|
+
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
21
|
+
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
22
|
+
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
23
|
+
import { COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeConfigErrorToCliError, computeFrameworkToBuildType, computeTargetAppDir, inferComputeTargetFromCwd, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs, selectComputeDeployTarget } from "../lib/app/compute-config.js";
|
|
24
|
+
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
25
|
+
import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
|
|
26
|
+
import { createDeployProgress, createDeployProgressState, createPromoteProgress } from "../lib/app/deploy-progress.js";
|
|
27
|
+
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
28
|
+
import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
|
|
29
|
+
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
30
|
+
import { resolveReadBranch } from "../lib/app/read-branch.js";
|
|
8
31
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
32
|
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
|
-
import {
|
|
11
|
-
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
12
|
-
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
|
-
import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
|
|
14
|
-
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
15
|
-
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
16
|
-
import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
17
|
-
import { bindProjectToDirectory, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
33
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
18
34
|
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
19
|
-
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
20
|
-
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
21
|
-
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
22
|
-
import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
23
|
-
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
24
35
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
25
36
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
26
37
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
27
|
-
import { access, readFile } from "node:fs/promises";
|
|
28
38
|
import path from "node:path";
|
|
39
|
+
import { access, readFile } from "node:fs/promises";
|
|
40
|
+
import { COMPUTE_REGIONS, ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, frameworkByKey, frameworkFromAlias, isConfigBackedBuildType } from "@prisma/compute-sdk/config";
|
|
41
|
+
import { Result, matchError } from "better-result";
|
|
29
42
|
import open from "open";
|
|
30
43
|
//#region src/controllers/app.ts
|
|
31
|
-
const DEPLOY_FRAMEWORKS = [
|
|
32
|
-
"nextjs",
|
|
33
|
-
"hono",
|
|
34
|
-
"tanstack-start",
|
|
35
|
-
"bun"
|
|
36
|
-
];
|
|
37
|
-
const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
|
|
38
44
|
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
39
45
|
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
40
46
|
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
47
|
+
const COMPUTE_REGION_IDS = new Set(COMPUTE_REGIONS);
|
|
41
48
|
function isRealMode(context) {
|
|
42
49
|
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
43
50
|
}
|
|
44
|
-
async function runAppBuild(context,
|
|
45
|
-
const
|
|
46
|
-
|
|
51
|
+
async function runAppBuild(context, options) {
|
|
52
|
+
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
|
|
53
|
+
const merged = mergeComputeLocalInputs({
|
|
54
|
+
cli: {
|
|
55
|
+
entrypoint: options?.entrypoint,
|
|
56
|
+
buildType: options?.buildType
|
|
57
|
+
},
|
|
58
|
+
target: compute.target
|
|
59
|
+
});
|
|
60
|
+
const appDir = await resolveComputeAppDir(context, compute);
|
|
61
|
+
let buildType = normalizeBuildType(merged.buildType);
|
|
62
|
+
if (compute.target?.build && buildType === "auto") {
|
|
63
|
+
const detected = await detectDeployFramework(appDir, context.runtime.signal);
|
|
64
|
+
if (!detected) throw frameworkNotDetectedError(appDir);
|
|
65
|
+
buildType = detected.buildType;
|
|
66
|
+
}
|
|
67
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
|
|
68
|
+
if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
|
|
69
|
+
const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
|
|
70
|
+
appPath: appDir,
|
|
71
|
+
buildType,
|
|
72
|
+
configured: compute.target.build,
|
|
73
|
+
configPath: compute.config.configPath,
|
|
74
|
+
signal: context.runtime.signal
|
|
75
|
+
})).settings : void 0;
|
|
47
76
|
try {
|
|
48
|
-
const { artifact, buildType: actualBuildType } = await
|
|
49
|
-
appPath:
|
|
50
|
-
entrypoint,
|
|
51
|
-
buildType
|
|
77
|
+
const { artifact, buildType: actualBuildType } = await executeAppBuild({
|
|
78
|
+
appPath: appDir,
|
|
79
|
+
entrypoint: merged.entrypoint,
|
|
80
|
+
buildType,
|
|
81
|
+
buildSettings,
|
|
82
|
+
signal: context.runtime.signal
|
|
52
83
|
});
|
|
53
84
|
return {
|
|
54
85
|
command: "app.build",
|
|
@@ -61,29 +92,46 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
|
|
|
61
92
|
nextSteps: ["prisma-cli app deploy"]
|
|
62
93
|
};
|
|
63
94
|
} catch (error) {
|
|
64
|
-
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 ${
|
|
95
|
+
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");
|
|
65
96
|
throw buildFailedError("Local app build failed", error);
|
|
66
97
|
}
|
|
67
98
|
}
|
|
68
|
-
async function runAppRun(context,
|
|
99
|
+
async function runAppRun(context, options) {
|
|
69
100
|
if (context.flags.json) throw usageError("App run does not support --json", "This command streams the framework dev server directly and cannot return structured JSON.", "Rerun without --json to pass framework logs through directly.", ["prisma-cli app run"], "app");
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
101
|
+
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
|
|
102
|
+
const merged = mergeComputeLocalInputs({
|
|
103
|
+
cli: {
|
|
104
|
+
entrypoint: options?.entrypoint,
|
|
105
|
+
buildType: options?.buildType,
|
|
106
|
+
port: options?.port
|
|
107
|
+
},
|
|
108
|
+
target: compute.target
|
|
109
|
+
});
|
|
110
|
+
if (merged.buildTypeFromConfig && compute.target?.framework && !frameworkByKey(compute.target.framework).hasLocalDevServer) throw usageError(`App run does not support the ${compute.target?.framework} framework yet`, `${compute.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1} sets a framework that has no local dev server in the current preview.`, "Run the framework dev server directly, or pass --build-type nextjs or --build-type bun to override.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
|
|
111
|
+
const appDir = await resolveComputeAppDir(context, compute);
|
|
112
|
+
const buildType = normalizeBuildType(merged.buildType);
|
|
113
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
|
|
114
|
+
const port = parseLocalPort(merged.port);
|
|
115
|
+
const framework = await resolveLocalRunFramework(context, {
|
|
116
|
+
requestedBuildType: buildType,
|
|
117
|
+
configFramework: compute.target?.framework ?? null,
|
|
118
|
+
appDir
|
|
119
|
+
});
|
|
120
|
+
const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
|
|
74
121
|
let runResult;
|
|
75
122
|
try {
|
|
76
123
|
runResult = await runLocalApp({
|
|
77
|
-
appPath:
|
|
78
|
-
buildType:
|
|
124
|
+
appPath: appDir,
|
|
125
|
+
buildType: framework.buildType,
|
|
79
126
|
entrypoint,
|
|
80
127
|
port,
|
|
81
|
-
env: context.runtime.env
|
|
128
|
+
env: context.runtime.env,
|
|
129
|
+
signal: context.runtime.signal
|
|
82
130
|
});
|
|
83
131
|
} catch (error) {
|
|
84
132
|
throw runFailedError("Local app run failed", error);
|
|
85
133
|
}
|
|
86
|
-
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM")
|
|
134
|
+
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") throw new DOMException("Command canceled", "AbortError");
|
|
87
135
|
else if (runResult.exitCode !== 0) throw runFailedError("Local app run failed", `The ${formatFrameworkName(runResult.framework)} process exited with code ${runResult.exitCode}.`, runResult.exitCode);
|
|
88
136
|
return {
|
|
89
137
|
command: "app.run",
|
|
@@ -99,6 +147,107 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
|
|
|
99
147
|
}
|
|
100
148
|
async function runAppDeploy(context, appName, options) {
|
|
101
149
|
ensurePreviewAppMode(context);
|
|
150
|
+
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
151
|
+
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
|
|
152
|
+
const config = loaded.value;
|
|
153
|
+
const plan = planAppDeploy({
|
|
154
|
+
config,
|
|
155
|
+
requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
|
|
156
|
+
hasCreateProject: options?.createProjectName !== void 0
|
|
157
|
+
});
|
|
158
|
+
if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
|
|
159
|
+
return runSingleAppDeploy(context, appName, options, config);
|
|
160
|
+
}
|
|
161
|
+
async function runAppDeployAll(context, config, plannedTargets, appName, options) {
|
|
162
|
+
assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options);
|
|
163
|
+
const deployments = [];
|
|
164
|
+
const warnings = [];
|
|
165
|
+
for (const planned of plannedTargets) {
|
|
166
|
+
maybeRenderDeployAllTargetHeader(context, planned);
|
|
167
|
+
const targetOptions = {
|
|
168
|
+
...options,
|
|
169
|
+
configTarget: planned.targetKey,
|
|
170
|
+
createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
|
|
171
|
+
};
|
|
172
|
+
try {
|
|
173
|
+
const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
|
|
174
|
+
deployments.push({
|
|
175
|
+
target: planned.targetKey,
|
|
176
|
+
result: single.result
|
|
177
|
+
});
|
|
178
|
+
warnings.push(...single.warnings);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
throw deployAllFailedError(error, plannedTargets, planned.index, deployments);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
command: "app.deploy",
|
|
185
|
+
result: { deployments },
|
|
186
|
+
warnings,
|
|
187
|
+
nextSteps: ["prisma-cli app list-deploys <app>"]
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options) {
|
|
191
|
+
const used = perAppInputsForDeployAll({
|
|
192
|
+
appName,
|
|
193
|
+
framework: options?.framework,
|
|
194
|
+
entrypoint: options?.entrypoint,
|
|
195
|
+
httpPort: options?.httpPort,
|
|
196
|
+
region: options?.region,
|
|
197
|
+
envAssignments: options?.envAssignments,
|
|
198
|
+
appIdEnvVar: {
|
|
199
|
+
name: PRISMA_APP_ID_ENV_VAR,
|
|
200
|
+
value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
if (used.length === 0) return;
|
|
204
|
+
const targetKeys = plannedTargets.map((target) => target.targetKey);
|
|
205
|
+
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");
|
|
206
|
+
}
|
|
207
|
+
function maybeRenderDeployAllTargetHeader(context, planned) {
|
|
208
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
209
|
+
context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
|
|
210
|
+
}
|
|
211
|
+
function deployAllFailedError(error, plannedTargets, failedIndex, deployments) {
|
|
212
|
+
if (!(error instanceof CliError)) return error;
|
|
213
|
+
const failure = describeDeployAllFailure({
|
|
214
|
+
targetKeys: plannedTargets.map((target) => target.targetKey),
|
|
215
|
+
failedIndex,
|
|
216
|
+
completed: deployments.map(({ target, result }) => ({
|
|
217
|
+
target,
|
|
218
|
+
deploymentId: result.deployment.id,
|
|
219
|
+
url: result.deployment.url
|
|
220
|
+
}))
|
|
221
|
+
});
|
|
222
|
+
const contextSentence = failure.contextLines.join(" ");
|
|
223
|
+
return new CliError({
|
|
224
|
+
code: error.code,
|
|
225
|
+
domain: error.domain,
|
|
226
|
+
summary: error.summary,
|
|
227
|
+
why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
|
|
228
|
+
fix: error.fix,
|
|
229
|
+
debug: error.debug,
|
|
230
|
+
where: error.where,
|
|
231
|
+
meta: {
|
|
232
|
+
...error.meta,
|
|
233
|
+
deployAll: {
|
|
234
|
+
failedTarget: failure.failedTarget,
|
|
235
|
+
completed: failure.completed,
|
|
236
|
+
notAttempted: failure.notAttempted
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
docsUrl: error.docsUrl,
|
|
240
|
+
exitCode: error.exitCode,
|
|
241
|
+
nextSteps: error.nextSteps,
|
|
242
|
+
nextActions: error.nextActions,
|
|
243
|
+
humanLines: error.humanLines ? [
|
|
244
|
+
...error.humanLines,
|
|
245
|
+
"",
|
|
246
|
+
...failure.contextLines
|
|
247
|
+
] : void 0
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
102
251
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
103
252
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
104
253
|
assertExclusiveDeployProjectInputs({
|
|
@@ -106,14 +255,30 @@ async function runAppDeploy(context, appName, options) {
|
|
|
106
255
|
createProjectName: options?.createProjectName,
|
|
107
256
|
envProjectId
|
|
108
257
|
});
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
258
|
+
const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
|
|
259
|
+
const merged = mergeComputeDeployInputs({
|
|
260
|
+
cli: {
|
|
261
|
+
framework: options?.framework,
|
|
262
|
+
entrypoint: options?.entrypoint,
|
|
263
|
+
httpPort: options?.httpPort,
|
|
264
|
+
region: options?.region,
|
|
265
|
+
envInputs: options?.envAssignments
|
|
266
|
+
},
|
|
267
|
+
target: computeConfig.target,
|
|
268
|
+
configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1
|
|
269
|
+
});
|
|
270
|
+
const appDir = await resolveComputeAppDir(context, computeConfig);
|
|
271
|
+
const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
|
|
272
|
+
const agentSetupWarnings = await maybePromptForAgentSetup(context, projectDir);
|
|
273
|
+
const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(projectDir, context.runtime.signal);
|
|
274
|
+
if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
|
|
275
|
+
const localPin = localPinReadResult.value;
|
|
112
276
|
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
113
|
-
if (
|
|
277
|
+
if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
|
|
278
|
+
const deployRegion = normalizeDeployRegionInput(merged.region);
|
|
114
279
|
assertSupportedEntrypointForRequestedDeployShape({
|
|
115
|
-
requestedFramework:
|
|
116
|
-
entrypoint:
|
|
280
|
+
requestedFramework: merged.framework?.value,
|
|
281
|
+
entrypoint: merged.entrypoint?.value
|
|
117
282
|
});
|
|
118
283
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
119
284
|
branch,
|
|
@@ -123,25 +288,33 @@ async function runAppDeploy(context, appName, options) {
|
|
|
123
288
|
});
|
|
124
289
|
let localPinResult;
|
|
125
290
|
if (target.localPinAction) {
|
|
126
|
-
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
127
|
-
|
|
128
|
-
|
|
291
|
+
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
|
|
292
|
+
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
293
|
+
const projectSetup = setupResult.value;
|
|
294
|
+
localPinResult = projectSetup.localPin;
|
|
295
|
+
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
129
296
|
}
|
|
130
297
|
let framework = await resolveDeployFramework(context, {
|
|
131
|
-
requestedFramework:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
298
|
+
requestedFramework: merged.framework?.value,
|
|
299
|
+
requestedFrameworkAnnotation: merged.framework?.annotation,
|
|
300
|
+
entrypoint: merged.entrypoint?.value,
|
|
301
|
+
entrypointAnnotation: merged.entrypoint?.annotation,
|
|
302
|
+
appDir
|
|
303
|
+
});
|
|
304
|
+
let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
|
|
305
|
+
assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
|
|
306
|
+
const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
|
|
137
307
|
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
138
308
|
explicitAppName: appName,
|
|
139
309
|
explicitAppId: envAppId,
|
|
310
|
+
configAppName: merged.configAppName,
|
|
311
|
+
configRegion: deployRegion,
|
|
140
312
|
firstDeploy: Boolean(target.localPinAction),
|
|
141
|
-
inferName: () => inferTargetName(context.runtime.
|
|
313
|
+
inferName: () => inferTargetName(appDir, context.runtime.signal)
|
|
142
314
|
});
|
|
143
315
|
await maybeRenderDeploySetupBlock(context, {
|
|
144
316
|
includeDirectory: !target.localPinAction,
|
|
317
|
+
appDir,
|
|
145
318
|
projectName: target.project.name,
|
|
146
319
|
branchName: target.branch.name,
|
|
147
320
|
appName: selectedApp.displayName
|
|
@@ -150,20 +323,41 @@ async function runAppDeploy(context, appName, options) {
|
|
|
150
323
|
framework,
|
|
151
324
|
runtime,
|
|
152
325
|
firstDeploy: selectedApp.firstDeploy,
|
|
153
|
-
explicitFramework: Boolean(
|
|
154
|
-
explicitEntrypoint: Boolean(
|
|
155
|
-
explicitHttpPort: Boolean(
|
|
326
|
+
explicitFramework: Boolean(merged.framework),
|
|
327
|
+
explicitEntrypoint: Boolean(merged.entrypoint),
|
|
328
|
+
explicitHttpPort: Boolean(merged.httpPort)
|
|
156
329
|
});
|
|
157
330
|
framework = customized.framework;
|
|
158
331
|
runtime = customized.runtime;
|
|
332
|
+
const noPromote = options?.noPromote === true;
|
|
333
|
+
const productionDeployGate = noPromote ? { firstProductionDeploy: false } : await enforceProductionDeployGate(context, provider, {
|
|
334
|
+
appId: selectedApp.appId,
|
|
335
|
+
appName: selectedApp.displayName,
|
|
336
|
+
branchKind: target.branch.kind,
|
|
337
|
+
prod: options?.prod === true
|
|
338
|
+
});
|
|
159
339
|
const buildType = framework.buildType;
|
|
160
|
-
assertSupportedEntrypoint(buildType,
|
|
161
|
-
const entrypoint = await resolveDeployEntrypoint(
|
|
340
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
|
|
341
|
+
const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
|
|
342
|
+
const buildSettingsResolution = await resolveDeployBuildSettings({
|
|
343
|
+
computeConfig,
|
|
344
|
+
appDir,
|
|
345
|
+
buildType,
|
|
346
|
+
signal: context.runtime.signal
|
|
347
|
+
});
|
|
348
|
+
const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
|
|
349
|
+
maybeRenderDeployBuildSettings(context, buildSettingsResolution);
|
|
162
350
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
163
|
-
const
|
|
351
|
+
const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
|
|
352
|
+
db: options?.db,
|
|
353
|
+
providedEnvVars: envVars,
|
|
354
|
+
firstProductionDeploy: productionDeployGate.firstProductionDeploy,
|
|
355
|
+
projectDir
|
|
356
|
+
});
|
|
357
|
+
const progressState = createDeployProgressState();
|
|
164
358
|
const deployStartedAt = Date.now();
|
|
165
359
|
const deployResult = await provider.deployApp({
|
|
166
|
-
cwd:
|
|
360
|
+
cwd: appDir,
|
|
167
361
|
projectId,
|
|
168
362
|
branchName: target.branch.name,
|
|
169
363
|
appId: selectedApp.appId,
|
|
@@ -171,10 +365,13 @@ async function runAppDeploy(context, appName, options) {
|
|
|
171
365
|
region: selectedApp.region,
|
|
172
366
|
entrypoint,
|
|
173
367
|
buildType,
|
|
368
|
+
buildSettings: buildSettingsResolution.settings,
|
|
174
369
|
portMapping,
|
|
175
370
|
envVars,
|
|
371
|
+
skipPromote: noPromote,
|
|
176
372
|
interaction: void 0,
|
|
177
|
-
|
|
373
|
+
signal: context.runtime.signal,
|
|
374
|
+
progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
|
|
178
375
|
}).catch((error) => {
|
|
179
376
|
throw appDeployFailedError(error, progressState);
|
|
180
377
|
});
|
|
@@ -183,41 +380,93 @@ async function runAppDeploy(context, appName, options) {
|
|
|
183
380
|
id: deployResult.app.id,
|
|
184
381
|
name: deployResult.app.name
|
|
185
382
|
});
|
|
186
|
-
|
|
383
|
+
const knownLiveDeploymentId = deployResult.promoted ? deployResult.deployment.id : deployResult.app.liveDeploymentId;
|
|
384
|
+
if (knownLiveDeploymentId) await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, knownLiveDeploymentId);
|
|
187
385
|
return {
|
|
188
386
|
command: "app.deploy",
|
|
189
387
|
result: {
|
|
190
388
|
workspace: target.workspace,
|
|
191
389
|
project: target.project,
|
|
192
|
-
branch: target.branch,
|
|
390
|
+
branch: toResultBranch(target.branch),
|
|
193
391
|
resolution: target.resolution,
|
|
392
|
+
branchDatabase: branchDatabaseSetup.result,
|
|
194
393
|
app: {
|
|
195
394
|
id: deployResult.app.id,
|
|
196
395
|
name: deployResult.app.name
|
|
197
396
|
},
|
|
198
397
|
deployment: deployResult.deployment,
|
|
398
|
+
promoted: deployResult.promoted,
|
|
399
|
+
deploySettings: {
|
|
400
|
+
config: {
|
|
401
|
+
path: buildSettingsResolution.relativeConfigPath,
|
|
402
|
+
status: buildSettingsResolution.status
|
|
403
|
+
},
|
|
404
|
+
buildCommand: {
|
|
405
|
+
value: buildSettingsResolution.settings.buildCommand,
|
|
406
|
+
source: buildSettingsResolution.settings.buildCommandSource
|
|
407
|
+
},
|
|
408
|
+
outputDirectory: {
|
|
409
|
+
value: buildSettingsResolution.settings.outputDirectory,
|
|
410
|
+
source: buildSettingsResolution.settings.outputDirectorySource
|
|
411
|
+
},
|
|
412
|
+
framework: {
|
|
413
|
+
key: framework.key,
|
|
414
|
+
buildType,
|
|
415
|
+
name: framework.displayName,
|
|
416
|
+
source: framework.annotation
|
|
417
|
+
},
|
|
418
|
+
entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
|
|
419
|
+
httpPort: runtime.port,
|
|
420
|
+
region: deployResult.app.region ?? selectedApp.region ?? null,
|
|
421
|
+
envVars: envVarNames(envVars)
|
|
422
|
+
},
|
|
199
423
|
durationMs: deployDurationMs,
|
|
200
424
|
localPin: localPinResult
|
|
201
425
|
},
|
|
202
|
-
warnings: [
|
|
203
|
-
|
|
426
|
+
warnings: [
|
|
427
|
+
...agentSetupWarnings,
|
|
428
|
+
...legacyWarnings,
|
|
429
|
+
...branchDatabaseSetup.warnings
|
|
430
|
+
],
|
|
431
|
+
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}`]
|
|
204
432
|
};
|
|
205
433
|
}
|
|
206
|
-
async function
|
|
434
|
+
async function resolveDeployBuildSettings(options) {
|
|
435
|
+
const { computeConfig, appDir, buildType, signal } = options;
|
|
436
|
+
if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
|
|
437
|
+
if (computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType)) return resolveConfiguredAppBuildSettings({
|
|
438
|
+
appPath: appDir,
|
|
439
|
+
buildType,
|
|
440
|
+
configured: computeConfig.target.build,
|
|
441
|
+
configPath: computeConfig.config.configPath,
|
|
442
|
+
signal
|
|
443
|
+
});
|
|
444
|
+
return resolveInferredAppBuildSettings({
|
|
445
|
+
appPath: appDir,
|
|
446
|
+
buildType,
|
|
447
|
+
signal
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
async function runAppListDeploys(context, appName, projectRef, configTarget) {
|
|
207
451
|
ensurePreviewAppMode(context);
|
|
208
|
-
const
|
|
209
|
-
const
|
|
452
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "list-deploys");
|
|
453
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
454
|
+
commandName: "app list-deploys",
|
|
455
|
+
projectDir: compute.projectDir
|
|
456
|
+
});
|
|
457
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
|
|
210
458
|
if (!selectedApp) return {
|
|
211
459
|
command: "app.list-deploys",
|
|
212
460
|
result: {
|
|
213
461
|
projectId,
|
|
462
|
+
verboseContext: toAppVerboseContext(target),
|
|
214
463
|
app: null,
|
|
215
464
|
deployments: []
|
|
216
465
|
},
|
|
217
466
|
warnings: [],
|
|
218
467
|
nextSteps: ["prisma-cli app deploy"]
|
|
219
468
|
};
|
|
220
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
469
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
221
470
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app deploy"]);
|
|
222
471
|
});
|
|
223
472
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -230,6 +479,7 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
230
479
|
command: "app.list-deploys",
|
|
231
480
|
result: {
|
|
232
481
|
projectId,
|
|
482
|
+
verboseContext: toAppVerboseContext(target),
|
|
233
483
|
app: {
|
|
234
484
|
id: deploymentsResult.app.id,
|
|
235
485
|
name: deploymentsResult.app.name
|
|
@@ -240,14 +490,19 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
240
490
|
nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
|
|
241
491
|
};
|
|
242
492
|
}
|
|
243
|
-
async function runAppShow(context, appName, projectRef) {
|
|
493
|
+
async function runAppShow(context, appName, projectRef, configTarget) {
|
|
244
494
|
ensurePreviewAppMode(context);
|
|
245
|
-
const
|
|
246
|
-
const
|
|
495
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "show");
|
|
496
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
497
|
+
commandName: "app show",
|
|
498
|
+
projectDir: compute.projectDir
|
|
499
|
+
});
|
|
500
|
+
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
|
|
247
501
|
if (!selectedApp) return {
|
|
248
502
|
command: "app.show",
|
|
249
503
|
result: {
|
|
250
504
|
projectId,
|
|
505
|
+
verboseContext: toAppVerboseContext(target),
|
|
251
506
|
app: null,
|
|
252
507
|
liveDeployment: null,
|
|
253
508
|
liveUrl: null,
|
|
@@ -256,7 +511,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
256
511
|
warnings: [],
|
|
257
512
|
nextSteps: ["prisma-cli app deploy"]
|
|
258
513
|
};
|
|
259
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
514
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
260
515
|
throw deployFailedError("Failed to inspect app", error, ["prisma-cli app list-deploys"]);
|
|
261
516
|
});
|
|
262
517
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -270,6 +525,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
270
525
|
command: "app.show",
|
|
271
526
|
result: {
|
|
272
527
|
projectId,
|
|
528
|
+
verboseContext: toAppVerboseContext(target),
|
|
273
529
|
app: {
|
|
274
530
|
id: deploymentsResult.app.id,
|
|
275
531
|
name: deploymentsResult.app.name
|
|
@@ -284,7 +540,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
284
540
|
}
|
|
285
541
|
async function runAppShowDeploy(context, deploymentId) {
|
|
286
542
|
ensurePreviewAppMode(context);
|
|
287
|
-
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId).catch((error) => {
|
|
543
|
+
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
288
544
|
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
289
545
|
});
|
|
290
546
|
if (!deployment) throw new CliError({
|
|
@@ -316,12 +572,17 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
316
572
|
nextSteps: []
|
|
317
573
|
};
|
|
318
574
|
}
|
|
319
|
-
async function runAppOpen(context, appName, projectRef) {
|
|
575
|
+
async function runAppOpen(context, appName, projectRef, configTarget) {
|
|
320
576
|
ensurePreviewAppMode(context);
|
|
321
|
-
const
|
|
577
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "open");
|
|
578
|
+
appName = appName ?? compute.configAppName;
|
|
579
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
580
|
+
commandName: "app open",
|
|
581
|
+
projectDir: compute.projectDir
|
|
582
|
+
});
|
|
322
583
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
323
584
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
324
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
585
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
325
586
|
throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
|
|
326
587
|
});
|
|
327
588
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -334,11 +595,16 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
334
595
|
if (!liveDeployment) throw noDeploymentsError("No deployments available to open", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
|
|
335
596
|
if (!deploymentsResult.app.liveUrl) throw featureUnavailableError("Live URL is not available for the selected app", "Deployments exist, but the provider does not expose a stable live service URL for this app yet.", "Run prisma-cli app show to inspect the current deployment state and try again after the app reports a live URL.", ["prisma-cli app show"], "app");
|
|
336
597
|
const shouldOpen = canPrompt(context);
|
|
337
|
-
if (shouldOpen)
|
|
598
|
+
if (shouldOpen) {
|
|
599
|
+
context.runtime.signal.throwIfAborted();
|
|
600
|
+
await open(deploymentsResult.app.liveUrl);
|
|
601
|
+
context.runtime.signal.throwIfAborted();
|
|
602
|
+
}
|
|
338
603
|
return {
|
|
339
604
|
command: "app.open",
|
|
340
605
|
result: {
|
|
341
606
|
projectId,
|
|
607
|
+
verboseContext: toAppVerboseContext(target),
|
|
342
608
|
app: {
|
|
343
609
|
id: deploymentsResult.app.id,
|
|
344
610
|
name: deploymentsResult.app.name
|
|
@@ -355,7 +621,8 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
355
621
|
const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
|
|
356
622
|
const added = await target.provider.addDomain({
|
|
357
623
|
appId: target.app.id,
|
|
358
|
-
hostname: normalizedHostname
|
|
624
|
+
hostname: normalizedHostname,
|
|
625
|
+
signal: context.runtime.signal
|
|
359
626
|
}).catch((error) => {
|
|
360
627
|
throw domainCommandError("add", error, normalizedHostname);
|
|
361
628
|
});
|
|
@@ -373,8 +640,8 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
373
640
|
async function runAppDomainShow(context, hostname, options) {
|
|
374
641
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
375
642
|
const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
|
|
376
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
|
|
377
|
-
const detail = await target.provider.showDomain(domain.id).catch((error) => {
|
|
643
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show", context.runtime.signal);
|
|
644
|
+
const detail = await target.provider.showDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
378
645
|
throw domainCommandError("show", error, normalizedHostname);
|
|
379
646
|
});
|
|
380
647
|
return {
|
|
@@ -390,9 +657,9 @@ async function runAppDomainShow(context, hostname, options) {
|
|
|
390
657
|
async function runAppDomainRemove(context, hostname, options) {
|
|
391
658
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
392
659
|
const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
|
|
393
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
|
|
660
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove", context.runtime.signal);
|
|
394
661
|
await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
|
|
395
|
-
await target.provider.removeDomain(domain.id).catch((error) => {
|
|
662
|
+
await target.provider.removeDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
396
663
|
throw domainCommandError("remove", error, normalizedHostname);
|
|
397
664
|
});
|
|
398
665
|
return {
|
|
@@ -409,8 +676,8 @@ async function runAppDomainRemove(context, hostname, options) {
|
|
|
409
676
|
async function runAppDomainRetry(context, hostname, options) {
|
|
410
677
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
411
678
|
const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
|
|
412
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
|
|
413
|
-
const retried = await target.provider.retryDomain(domain.id).catch((error) => {
|
|
679
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry", context.runtime.signal);
|
|
680
|
+
const retried = await target.provider.retryDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
414
681
|
throw domainCommandError("retry", error, normalizedHostname);
|
|
415
682
|
});
|
|
416
683
|
return {
|
|
@@ -427,7 +694,7 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
427
694
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
428
695
|
const timeoutMs = parseDomainWaitTimeout(options?.timeout);
|
|
429
696
|
const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
|
|
430
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
|
|
697
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait", context.runtime.signal);
|
|
431
698
|
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
|
|
432
699
|
`app domain wait -> Waiting for ${normalizedHostname} to become active.`,
|
|
433
700
|
"",
|
|
@@ -470,21 +737,25 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
470
737
|
exitCode: 1,
|
|
471
738
|
nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
|
|
472
739
|
});
|
|
473
|
-
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)));
|
|
474
|
-
current = await target.provider.showDomain(current.id).catch((error) => {
|
|
740
|
+
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)), context.runtime.signal);
|
|
741
|
+
current = await target.provider.showDomain(current.id, { signal: context.runtime.signal }).catch((error) => {
|
|
475
742
|
throw domainCommandError("wait", error, normalizedHostname);
|
|
476
743
|
});
|
|
477
744
|
}
|
|
478
745
|
}
|
|
479
|
-
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
746
|
+
async function runAppLogs(context, appName, deploymentId, projectRef, configTarget) {
|
|
480
747
|
ensurePreviewAppMode(context);
|
|
481
|
-
const
|
|
748
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "logs");
|
|
749
|
+
appName = appName ?? compute.configAppName;
|
|
750
|
+
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
751
|
+
commandName: "app logs",
|
|
752
|
+
projectDir: compute.projectDir
|
|
753
|
+
});
|
|
482
754
|
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
483
755
|
if (!context.flags.json && !context.flags.quiet) {
|
|
484
756
|
const lines = renderCommandHeader(context.ui, {
|
|
485
757
|
commandLabel: "app logs",
|
|
486
758
|
description: "Streaming logs for the selected deployment.",
|
|
487
|
-
docsPath: "docs/product/command-spec.md#prisma-cli-app-logs---app-name---deployment-id",
|
|
488
759
|
rows: [
|
|
489
760
|
{
|
|
490
761
|
key: "project",
|
|
@@ -504,6 +775,7 @@ async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
|
504
775
|
}
|
|
505
776
|
await provider.streamDeploymentLogs({
|
|
506
777
|
deploymentId: target.deployment.id,
|
|
778
|
+
signal: context.runtime.signal,
|
|
507
779
|
onRecord: (record) => writeLogRecord(context, record)
|
|
508
780
|
}).catch((error) => {
|
|
509
781
|
throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
|
|
@@ -513,7 +785,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
513
785
|
if (appName) {
|
|
514
786
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
515
787
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
516
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
788
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
517
789
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
518
790
|
});
|
|
519
791
|
const deployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
|
|
@@ -526,7 +798,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
526
798
|
deployment
|
|
527
799
|
};
|
|
528
800
|
}
|
|
529
|
-
const shown = await provider.showDeployment(deploymentId).catch((error) => {
|
|
801
|
+
const shown = await provider.showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
530
802
|
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
531
803
|
});
|
|
532
804
|
if (!shown) throw new CliError({
|
|
@@ -569,7 +841,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
569
841
|
async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
|
|
570
842
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
571
843
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
572
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
844
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
573
845
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
574
846
|
});
|
|
575
847
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -600,11 +872,16 @@ function writeLogRecord(context, record) {
|
|
|
600
872
|
if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
|
|
601
873
|
}
|
|
602
874
|
}
|
|
603
|
-
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
875
|
+
async function runAppPromote(context, deploymentId, appName, projectRef, configTarget) {
|
|
604
876
|
ensurePreviewAppMode(context);
|
|
605
|
-
const
|
|
877
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "promote");
|
|
878
|
+
appName = appName ?? compute.configAppName;
|
|
879
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
880
|
+
commandName: "app promote",
|
|
881
|
+
projectDir: compute.projectDir
|
|
882
|
+
});
|
|
606
883
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
607
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
884
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
608
885
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
609
886
|
});
|
|
610
887
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -617,7 +894,8 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
617
894
|
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
618
895
|
appId: selectedApp.id,
|
|
619
896
|
deploymentId: targetDeployment.id,
|
|
620
|
-
|
|
897
|
+
signal: context.runtime.signal,
|
|
898
|
+
progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
621
899
|
}).catch((error) => {
|
|
622
900
|
throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
|
|
623
901
|
});
|
|
@@ -626,6 +904,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
626
904
|
command: "app.promote",
|
|
627
905
|
result: {
|
|
628
906
|
projectId,
|
|
907
|
+
verboseContext: toAppVerboseContext(target),
|
|
629
908
|
app: {
|
|
630
909
|
id: deploymentsResult.app.id,
|
|
631
910
|
name: deploymentsResult.app.name
|
|
@@ -640,11 +919,16 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
640
919
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
641
920
|
};
|
|
642
921
|
}
|
|
643
|
-
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
922
|
+
async function runAppRollback(context, appName, deploymentId, projectRef, configTarget) {
|
|
644
923
|
ensurePreviewAppMode(context);
|
|
645
|
-
const
|
|
924
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "rollback");
|
|
925
|
+
appName = appName ?? compute.configAppName;
|
|
926
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
927
|
+
commandName: "app rollback",
|
|
928
|
+
projectDir: compute.projectDir
|
|
929
|
+
});
|
|
646
930
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
647
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
931
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
648
932
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
649
933
|
});
|
|
650
934
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -658,7 +942,8 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
658
942
|
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
659
943
|
appId: selectedApp.id,
|
|
660
944
|
deploymentId: targetDeployment.id,
|
|
661
|
-
|
|
945
|
+
signal: context.runtime.signal,
|
|
946
|
+
progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
662
947
|
}).catch((error) => {
|
|
663
948
|
throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
|
|
664
949
|
});
|
|
@@ -667,6 +952,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
667
952
|
command: "app.rollback",
|
|
668
953
|
result: {
|
|
669
954
|
projectId,
|
|
955
|
+
verboseContext: toAppVerboseContext(target),
|
|
670
956
|
app: {
|
|
671
957
|
id: deploymentsResult.app.id,
|
|
672
958
|
name: deploymentsResult.app.name
|
|
@@ -682,12 +968,17 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
682
968
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
683
969
|
};
|
|
684
970
|
}
|
|
685
|
-
async function runAppRemove(context, appName, projectRef) {
|
|
971
|
+
async function runAppRemove(context, appName, projectRef, configTarget) {
|
|
686
972
|
ensurePreviewAppMode(context);
|
|
687
|
-
const
|
|
973
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "remove");
|
|
974
|
+
appName = appName ?? compute.configAppName;
|
|
975
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
976
|
+
commandName: "app remove",
|
|
977
|
+
projectDir: compute.projectDir
|
|
978
|
+
});
|
|
688
979
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
689
980
|
await confirmAppRemoval(context, selectedApp);
|
|
690
|
-
const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
|
|
981
|
+
const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
691
982
|
throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
|
|
692
983
|
});
|
|
693
984
|
const warnings = await cleanupRemovedAppState(context, projectId, removedApp.id);
|
|
@@ -695,6 +986,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
695
986
|
command: "app.remove",
|
|
696
987
|
result: {
|
|
697
988
|
projectId,
|
|
989
|
+
verboseContext: toAppVerboseContext(target),
|
|
698
990
|
app: {
|
|
699
991
|
id: removedApp.id,
|
|
700
992
|
name: removedApp.name
|
|
@@ -707,6 +999,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
707
999
|
}
|
|
708
1000
|
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
709
1001
|
ensurePreviewAppMode(context);
|
|
1002
|
+
const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(/^app /, ""));
|
|
710
1003
|
const branch = resolveDomainBranch(options?.branchName);
|
|
711
1004
|
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
712
1005
|
code: "BRANCH_NOT_DEPLOYABLE",
|
|
@@ -722,10 +1015,11 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
722
1015
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
723
1016
|
branch,
|
|
724
1017
|
commandName,
|
|
725
|
-
envProjectId
|
|
1018
|
+
envProjectId,
|
|
1019
|
+
projectDir: compute.projectDir
|
|
726
1020
|
});
|
|
727
1021
|
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
728
|
-
explicitAppName: options?.appName,
|
|
1022
|
+
explicitAppName: options?.appName ?? compute.configAppName,
|
|
729
1023
|
explicitAppId: envAppId
|
|
730
1024
|
});
|
|
731
1025
|
await context.stateStore.setSelectedApp(projectId, {
|
|
@@ -738,7 +1032,7 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
738
1032
|
resultTarget: {
|
|
739
1033
|
workspace: target.workspace,
|
|
740
1034
|
project: target.project,
|
|
741
|
-
branch: target.branch,
|
|
1035
|
+
branch: toResultBranch(target.branch),
|
|
742
1036
|
app: {
|
|
743
1037
|
id: selectedApp.id,
|
|
744
1038
|
name: selectedApp.name
|
|
@@ -762,8 +1056,8 @@ async function resolveDomainAppSelection(context, projectId, apps, options) {
|
|
|
762
1056
|
if (selectedApp) return selectedApp;
|
|
763
1057
|
throw usageError("Custom domain requires an existing app on the production branch", "The resolved production branch does not have an app that can receive a custom domain.", "Deploy or promote an app to production first, then rerun the domain command.", ["prisma-cli app deploy --branch production", "prisma-cli app show"], "app");
|
|
764
1058
|
}
|
|
765
|
-
async function resolveDomainByHostname(provider, appId, hostname, command) {
|
|
766
|
-
const matched = (await provider.listDomains(appId).catch((error) => {
|
|
1059
|
+
async function resolveDomainByHostname(provider, appId, hostname, command, signal) {
|
|
1060
|
+
const matched = (await provider.listDomains(appId, { signal }).catch((error) => {
|
|
767
1061
|
throw domainCommandError(command, error, hostname);
|
|
768
1062
|
})).find((domain) => sameDomainHostname(domain.hostname, hostname));
|
|
769
1063
|
if (matched) return matched;
|
|
@@ -798,7 +1092,7 @@ function toAppDomainSummary(domain) {
|
|
|
798
1092
|
type: domain.type,
|
|
799
1093
|
url: domain.url,
|
|
800
1094
|
hostname: domain.hostname,
|
|
801
|
-
|
|
1095
|
+
appId: domain.appId,
|
|
802
1096
|
status: domain.status,
|
|
803
1097
|
foundryStatus: domain.foundryStatus,
|
|
804
1098
|
failureReason: domain.failureReason,
|
|
@@ -836,12 +1130,13 @@ async function confirmDomainRemoval(context, target, hostname) {
|
|
|
836
1130
|
if (!await confirmPrompt({
|
|
837
1131
|
input: context.runtime.stdin,
|
|
838
1132
|
output: context.output.stderr,
|
|
1133
|
+
signal: context.runtime.signal,
|
|
839
1134
|
message: `Detach ${hostname} from App "${target.app.name}"?`,
|
|
840
1135
|
initialValue: false
|
|
841
1136
|
})) 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");
|
|
842
1137
|
}
|
|
843
1138
|
function domainCommandError(command, error, hostname) {
|
|
844
|
-
if (error instanceof
|
|
1139
|
+
if (error instanceof DomainApiError) {
|
|
845
1140
|
if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
|
|
846
1141
|
if (command === "add" && error.status === 400) return new CliError({
|
|
847
1142
|
code: "DOMAIN_HOSTNAME_INVALID",
|
|
@@ -994,32 +1289,35 @@ function formatElapsed(milliseconds) {
|
|
|
994
1289
|
const remainingSeconds = seconds % 60;
|
|
995
1290
|
return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
|
|
996
1291
|
}
|
|
997
|
-
async function sleep(milliseconds) {
|
|
1292
|
+
async function sleep(milliseconds, signal) {
|
|
998
1293
|
if (milliseconds <= 0) return;
|
|
999
|
-
|
|
1294
|
+
signal.throwIfAborted();
|
|
1295
|
+
await new Promise((resolve, reject) => {
|
|
1296
|
+
const onAbort = () => {
|
|
1297
|
+
clearTimeout(timeout);
|
|
1298
|
+
reject(signal.reason);
|
|
1299
|
+
};
|
|
1300
|
+
const timeout = setTimeout(() => {
|
|
1301
|
+
signal.removeEventListener("abort", onAbort);
|
|
1302
|
+
resolve();
|
|
1303
|
+
}, milliseconds);
|
|
1304
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1305
|
+
});
|
|
1000
1306
|
}
|
|
1001
1307
|
async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
};
|
|
1012
|
-
return {
|
|
1013
|
-
appName: options.explicitAppName,
|
|
1014
|
-
region: PREVIEW_DEFAULT_REGION,
|
|
1015
|
-
displayName: options.explicitAppName,
|
|
1016
|
-
annotation: "set by --app",
|
|
1017
|
-
firstDeploy: options.firstDeploy
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1308
|
+
const newAppRegion = deployNewAppRegion(options.configRegion);
|
|
1309
|
+
if (options.explicitAppName) return resolveDeployAppByName(context, apps, {
|
|
1310
|
+
name: options.explicitAppName,
|
|
1311
|
+
matchedAnnotation: "set by --app",
|
|
1312
|
+
newAnnotation: "set by --app",
|
|
1313
|
+
requestedRegion: options.configRegion,
|
|
1314
|
+
newAppRegion,
|
|
1315
|
+
firstDeploy: options.firstDeploy
|
|
1316
|
+
});
|
|
1020
1317
|
if (options.explicitAppId) {
|
|
1021
1318
|
const matched = apps.find((app) => app.id === options.explicitAppId);
|
|
1022
1319
|
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");
|
|
1320
|
+
assertDeployRegionMatchesExistingApp(matched, options.configRegion);
|
|
1023
1321
|
return {
|
|
1024
1322
|
appId: matched.id,
|
|
1025
1323
|
displayName: matched.name,
|
|
@@ -1027,25 +1325,54 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
|
1027
1325
|
firstDeploy: options.firstDeploy
|
|
1028
1326
|
};
|
|
1029
1327
|
}
|
|
1328
|
+
if (options.configAppName) {
|
|
1329
|
+
const configName = options.configAppName;
|
|
1330
|
+
return resolveDeployAppByName(context, apps, {
|
|
1331
|
+
name: configName.value,
|
|
1332
|
+
matchedAnnotation: configName.annotation,
|
|
1333
|
+
newAnnotation: configName.annotation,
|
|
1334
|
+
requestedRegion: options.configRegion,
|
|
1335
|
+
newAppRegion,
|
|
1336
|
+
firstDeploy: options.firstDeploy
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1030
1339
|
const inferredName = await options.inferName();
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1340
|
+
const newAnnotation = inferredName.source === "package-name" ? "created from package.json" : "created from directory name";
|
|
1341
|
+
return resolveDeployAppByName(context, apps, {
|
|
1342
|
+
name: inferredName.name,
|
|
1343
|
+
matchedAnnotation: "existing app on this branch",
|
|
1344
|
+
newAnnotation,
|
|
1345
|
+
requestedRegion: options.configRegion,
|
|
1346
|
+
newAppRegion,
|
|
1038
1347
|
firstDeploy: options.firstDeploy
|
|
1039
|
-
};
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
async function resolveDeployAppByName(context, apps, options) {
|
|
1351
|
+
const matches = findAppsByName(apps, options.name);
|
|
1352
|
+
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.name, options.requestedRegion, options.newAppRegion, options.firstDeploy);
|
|
1353
|
+
const matched = matches[0];
|
|
1354
|
+
if (matched) {
|
|
1355
|
+
assertDeployRegionMatchesExistingApp(matched, options.requestedRegion);
|
|
1356
|
+
return {
|
|
1357
|
+
appId: matched.id,
|
|
1358
|
+
displayName: matched.name,
|
|
1359
|
+
annotation: options.matchedAnnotation,
|
|
1360
|
+
firstDeploy: options.firstDeploy
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1040
1363
|
return {
|
|
1041
|
-
appName:
|
|
1042
|
-
region:
|
|
1043
|
-
displayName:
|
|
1044
|
-
annotation:
|
|
1364
|
+
appName: options.name,
|
|
1365
|
+
region: options.newAppRegion,
|
|
1366
|
+
displayName: options.name,
|
|
1367
|
+
annotation: options.newAnnotation,
|
|
1045
1368
|
firstDeploy: options.firstDeploy
|
|
1046
1369
|
};
|
|
1047
1370
|
}
|
|
1048
|
-
|
|
1371
|
+
function assertDeployRegionMatchesExistingApp(app, requestedRegion) {
|
|
1372
|
+
if (requestedRegion?.annotation !== "set by --region" || !app.region || app.region === requestedRegion.value) return;
|
|
1373
|
+
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");
|
|
1374
|
+
}
|
|
1375
|
+
async function resolveAmbiguousDeployApp(context, matches, targetName, requestedRegion, newAppRegion, firstDeploy) {
|
|
1049
1376
|
if (canPrompt(context)) {
|
|
1050
1377
|
const createNew = "__create_new_app__";
|
|
1051
1378
|
const cancel = "__cancel__";
|
|
@@ -1071,11 +1398,12 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
|
|
|
1071
1398
|
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");
|
|
1072
1399
|
if (selected === createNew) return {
|
|
1073
1400
|
appName: targetName,
|
|
1074
|
-
region:
|
|
1401
|
+
region: newAppRegion,
|
|
1075
1402
|
displayName: targetName,
|
|
1076
1403
|
annotation: "created from package.json",
|
|
1077
1404
|
firstDeploy
|
|
1078
1405
|
};
|
|
1406
|
+
assertDeployRegionMatchesExistingApp(selected, requestedRegion);
|
|
1079
1407
|
return {
|
|
1080
1408
|
appId: selected.id,
|
|
1081
1409
|
displayName: selected.name,
|
|
@@ -1097,6 +1425,9 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
|
|
|
1097
1425
|
nextSteps: ["prisma-cli app deploy --app <name>"]
|
|
1098
1426
|
});
|
|
1099
1427
|
}
|
|
1428
|
+
function deployNewAppRegion(configRegion) {
|
|
1429
|
+
return configRegion?.value ?? "eu-central-1";
|
|
1430
|
+
}
|
|
1100
1431
|
async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
|
|
1101
1432
|
if (explicitAppName) {
|
|
1102
1433
|
const matched = findAppByName(apps, explicitAppName);
|
|
@@ -1139,6 +1470,7 @@ async function confirmAppRemoval(context, app) {
|
|
|
1139
1470
|
await textPrompt({
|
|
1140
1471
|
input: context.runtime.stdin,
|
|
1141
1472
|
output: context.output.stderr,
|
|
1473
|
+
signal: context.runtime.signal,
|
|
1142
1474
|
message: `Type ${app.name} to confirm app removal`,
|
|
1143
1475
|
placeholder: app.name,
|
|
1144
1476
|
validate: (value) => value === app.name ? void 0 : `Type "${app.name}" to confirm removal.`
|
|
@@ -1211,7 +1543,10 @@ function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
|
|
|
1211
1543
|
});
|
|
1212
1544
|
}
|
|
1213
1545
|
async function listApps(context, provider, projectId, branchName) {
|
|
1214
|
-
return provider.listApps(projectId, {
|
|
1546
|
+
return provider.listApps(projectId, {
|
|
1547
|
+
branchName,
|
|
1548
|
+
signal: context.runtime.signal
|
|
1549
|
+
}).then(sortApps).catch((error) => {
|
|
1215
1550
|
if (isMissingProjectError(error)) throw new CliError({
|
|
1216
1551
|
code: "PROJECT_NOT_FOUND",
|
|
1217
1552
|
domain: "project",
|
|
@@ -1229,20 +1564,20 @@ async function requirePreviewAppProvider(context) {
|
|
|
1229
1564
|
return provider;
|
|
1230
1565
|
}
|
|
1231
1566
|
async function requirePreviewAppProviderWithClient(context) {
|
|
1232
|
-
const client = await requireComputeAuth(context.runtime.env);
|
|
1567
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
1233
1568
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
1234
1569
|
return {
|
|
1235
1570
|
client,
|
|
1236
|
-
provider:
|
|
1571
|
+
provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
|
|
1237
1572
|
};
|
|
1238
1573
|
}
|
|
1239
|
-
function createPreviewLogAuthOptions(env) {
|
|
1574
|
+
function createPreviewLogAuthOptions(env, signal) {
|
|
1240
1575
|
const rawToken = env[SERVICE_TOKEN_ENV_VAR]?.trim();
|
|
1241
1576
|
if (rawToken) return {
|
|
1242
1577
|
baseUrl: getApiBaseUrl(env),
|
|
1243
1578
|
getToken: async () => rawToken
|
|
1244
1579
|
};
|
|
1245
|
-
const tokenStorage = new FileTokenStorage(env);
|
|
1580
|
+
const tokenStorage = new FileTokenStorage(env, signal);
|
|
1246
1581
|
return {
|
|
1247
1582
|
baseUrl: getApiBaseUrl(env),
|
|
1248
1583
|
getToken: async () => {
|
|
@@ -1275,20 +1610,29 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1275
1610
|
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1276
1611
|
const authState = await requireAuthenticatedAuthState(context);
|
|
1277
1612
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
1278
|
-
const
|
|
1613
|
+
const resolvedResult = await resolveProjectTarget({
|
|
1279
1614
|
context,
|
|
1280
1615
|
workspace: authState.workspace,
|
|
1281
1616
|
explicitProject,
|
|
1282
1617
|
envProjectId: options?.envProjectId,
|
|
1283
|
-
|
|
1618
|
+
projectDir: options?.projectDir,
|
|
1619
|
+
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
1284
1620
|
commandName: options?.commandName
|
|
1285
1621
|
});
|
|
1286
|
-
|
|
1622
|
+
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
1623
|
+
const resolved = resolvedResult.value;
|
|
1624
|
+
const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1625
|
+
const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
|
|
1626
|
+
projectId: resolved.project.id,
|
|
1627
|
+
branchName: requested.name,
|
|
1628
|
+
signal: context.runtime.signal
|
|
1629
|
+
});
|
|
1287
1630
|
return {
|
|
1288
1631
|
...resolved,
|
|
1289
|
-
branch: {
|
|
1290
|
-
|
|
1291
|
-
|
|
1632
|
+
branch: remoteBranch ?? {
|
|
1633
|
+
id: null,
|
|
1634
|
+
name: requested.name,
|
|
1635
|
+
kind: toBranchKind(requested.name)
|
|
1292
1636
|
}
|
|
1293
1637
|
};
|
|
1294
1638
|
}
|
|
@@ -1296,8 +1640,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1296
1640
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1297
1641
|
if (!workspace) throw workspaceRequiredError();
|
|
1298
1642
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1299
|
-
const projects = await listRealWorkspaceProjects(client, workspace);
|
|
1300
|
-
if (explicitProject) return
|
|
1643
|
+
const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
|
|
1644
|
+
if (explicitProject) return withRemoteDeployBranch(provider, {
|
|
1301
1645
|
workspace,
|
|
1302
1646
|
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1303
1647
|
resolution: {
|
|
@@ -1306,25 +1650,25 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1306
1650
|
targetNameSource: "explicit"
|
|
1307
1651
|
},
|
|
1308
1652
|
localPinAction: "linked"
|
|
1309
|
-
}, branch);
|
|
1653
|
+
}, branch, context.runtime.signal);
|
|
1310
1654
|
if (options.createProjectName) {
|
|
1311
1655
|
const projectName = options.createProjectName.trim();
|
|
1312
1656
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1313
|
-
return
|
|
1657
|
+
return withRemoteDeployBranch(provider, {
|
|
1314
1658
|
workspace,
|
|
1315
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
|
|
1659
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1316
1660
|
resolution: {
|
|
1317
1661
|
projectSource: "created",
|
|
1318
1662
|
targetName: projectName,
|
|
1319
1663
|
targetNameSource: "explicit"
|
|
1320
1664
|
},
|
|
1321
1665
|
localPinAction: "created"
|
|
1322
|
-
}, branch);
|
|
1666
|
+
}, branch, context.runtime.signal);
|
|
1323
1667
|
}
|
|
1324
1668
|
if (options.envProjectId) {
|
|
1325
1669
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1326
1670
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1327
|
-
return
|
|
1671
|
+
return withRemoteDeployBranch(provider, {
|
|
1328
1672
|
workspace,
|
|
1329
1673
|
project: toProjectSummary(project),
|
|
1330
1674
|
resolution: {
|
|
@@ -1332,14 +1676,18 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1332
1676
|
targetName: options.envProjectId,
|
|
1333
1677
|
targetNameSource: "env"
|
|
1334
1678
|
}
|
|
1335
|
-
}, branch);
|
|
1679
|
+
}, branch, context.runtime.signal);
|
|
1336
1680
|
}
|
|
1337
1681
|
const localPin = options.localPin;
|
|
1338
1682
|
if (localPin.kind === "present") {
|
|
1339
|
-
if (localPin.pin.workspaceId !== workspace.id) throw
|
|
1683
|
+
if (localPin.pin.workspaceId !== workspace.id) throw localProjectWorkspaceMismatchError({
|
|
1684
|
+
pinnedWorkspaceId: localPin.pin.workspaceId,
|
|
1685
|
+
pinnedProjectId: localPin.pin.projectId,
|
|
1686
|
+
activeWorkspace: workspace
|
|
1687
|
+
});
|
|
1340
1688
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
1341
1689
|
if (!project) throw localResolutionPinStaleError();
|
|
1342
|
-
return
|
|
1690
|
+
return withRemoteDeployBranch(provider, {
|
|
1343
1691
|
workspace,
|
|
1344
1692
|
project: toProjectSummary(project),
|
|
1345
1693
|
resolution: {
|
|
@@ -1347,10 +1695,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1347
1695
|
targetName: project.name,
|
|
1348
1696
|
targetNameSource: "local-pin"
|
|
1349
1697
|
}
|
|
1350
|
-
}, branch);
|
|
1698
|
+
}, branch, context.runtime.signal);
|
|
1351
1699
|
}
|
|
1352
1700
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
1353
|
-
if (platformMapping && platformMapping.workspace.id === workspace.id) return
|
|
1701
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
|
|
1354
1702
|
workspace,
|
|
1355
1703
|
project: toProjectSummary(platformMapping),
|
|
1356
1704
|
resolution: {
|
|
@@ -1358,15 +1706,15 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1358
1706
|
targetName: platformMapping.name,
|
|
1359
1707
|
targetNameSource: "platform-mapping"
|
|
1360
1708
|
}
|
|
1361
|
-
}, branch);
|
|
1362
|
-
if (canPrompt(context) && !context.flags.yes) return
|
|
1363
|
-
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd));
|
|
1709
|
+
}, branch, context.runtime.signal);
|
|
1710
|
+
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
|
|
1711
|
+
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1364
1712
|
}
|
|
1365
1713
|
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1366
1714
|
const setup = await promptForProjectSetupChoice({
|
|
1367
1715
|
context,
|
|
1368
1716
|
projects,
|
|
1369
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace),
|
|
1717
|
+
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
|
|
1370
1718
|
cancel: {
|
|
1371
1719
|
why: "Deploy needs a Project before it can continue.",
|
|
1372
1720
|
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
@@ -1384,8 +1732,11 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
|
|
|
1384
1732
|
localPinAction: setup.action
|
|
1385
1733
|
};
|
|
1386
1734
|
}
|
|
1387
|
-
async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
1388
|
-
const created = await provider.createProject({
|
|
1735
|
+
async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
|
|
1736
|
+
const created = await provider.createProject({
|
|
1737
|
+
name: projectName,
|
|
1738
|
+
signal
|
|
1739
|
+
}).catch((error) => {
|
|
1389
1740
|
throw projectCreateFailedError(error, projectName, workspace, {
|
|
1390
1741
|
nextSteps: [
|
|
1391
1742
|
"prisma-cli project list",
|
|
@@ -1402,18 +1753,46 @@ async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
|
1402
1753
|
workspace
|
|
1403
1754
|
};
|
|
1404
1755
|
}
|
|
1405
|
-
function
|
|
1756
|
+
async function withRemoteDeployBranch(provider, target, branch, signal) {
|
|
1757
|
+
const remoteBranch = await provider.resolveBranch(target.project.id, {
|
|
1758
|
+
branchName: branch.name,
|
|
1759
|
+
signal
|
|
1760
|
+
});
|
|
1406
1761
|
return {
|
|
1407
1762
|
...target,
|
|
1408
1763
|
branch: {
|
|
1409
|
-
|
|
1410
|
-
|
|
1764
|
+
id: remoteBranch.id,
|
|
1765
|
+
name: remoteBranch.name,
|
|
1766
|
+
kind: remoteBranch.role
|
|
1411
1767
|
}
|
|
1412
1768
|
};
|
|
1413
1769
|
}
|
|
1414
1770
|
function toBranchKind(name) {
|
|
1415
1771
|
return name === "production" || name === "main" ? "production" : "preview";
|
|
1416
1772
|
}
|
|
1773
|
+
function toResultBranch(branch) {
|
|
1774
|
+
return {
|
|
1775
|
+
id: branch.id,
|
|
1776
|
+
name: branch.name,
|
|
1777
|
+
kind: branch.kind
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
function toAppVerboseContext(target) {
|
|
1781
|
+
return {
|
|
1782
|
+
workspace: target.workspace,
|
|
1783
|
+
project: target.project,
|
|
1784
|
+
branch: target.branch,
|
|
1785
|
+
resolution: target.resolution
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
function toBranchDatabaseDeployBranch(branch) {
|
|
1789
|
+
if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
|
|
1790
|
+
return {
|
|
1791
|
+
id: branch.id,
|
|
1792
|
+
name: branch.name,
|
|
1793
|
+
kind: branch.kind
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1417
1796
|
function assertExclusiveDeployProjectInputs(options) {
|
|
1418
1797
|
const provided = [
|
|
1419
1798
|
options.projectRef ? "--project" : null,
|
|
@@ -1432,7 +1811,7 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1432
1811
|
name: explicitBranchName,
|
|
1433
1812
|
annotation: "set by --branch"
|
|
1434
1813
|
};
|
|
1435
|
-
const gitBranch = await readLocalGitBranch(context.runtime.cwd);
|
|
1814
|
+
const gitBranch = await readLocalGitBranch(context.runtime.cwd, context.runtime.signal);
|
|
1436
1815
|
if (gitBranch) return {
|
|
1437
1816
|
name: gitBranch,
|
|
1438
1817
|
annotation: "from local Git branch"
|
|
@@ -1442,45 +1821,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1442
1821
|
annotation: "default"
|
|
1443
1822
|
};
|
|
1444
1823
|
}
|
|
1445
|
-
async function
|
|
1446
|
-
|
|
1447
|
-
if (
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
if (
|
|
1451
|
-
|
|
1452
|
-
return null;
|
|
1824
|
+
async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
|
|
1825
|
+
let config;
|
|
1826
|
+
if (options?.preloaded !== void 0) config = options.preloaded;
|
|
1827
|
+
else {
|
|
1828
|
+
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
1829
|
+
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
|
|
1830
|
+
config = loaded.value;
|
|
1453
1831
|
}
|
|
1454
|
-
|
|
1832
|
+
if (!config) {
|
|
1833
|
+
if (configTarget) throw usageError(`App target "${configTarget}" requires a compute config file`, `No ${COMPUTE_CONFIG_FILENAME$1} exists in the current directory, so there are no named app targets.`, `Create ${COMPUTE_CONFIG_FILENAME$1} with an apps entry named "${configTarget}", or rerun without the target argument.`, [`prisma-cli app ${commandName}`], "app");
|
|
1834
|
+
return {
|
|
1835
|
+
config: null,
|
|
1836
|
+
target: null
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
|
|
1840
|
+
const selected = selectComputeDeployTarget(config, requestedTarget);
|
|
1841
|
+
if (selected.isErr()) {
|
|
1842
|
+
if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
|
|
1843
|
+
config,
|
|
1844
|
+
target: null
|
|
1845
|
+
};
|
|
1846
|
+
throw computeConfigErrorToCliError(selected.error, commandName);
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
config,
|
|
1850
|
+
target: selected.value
|
|
1851
|
+
};
|
|
1455
1852
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1853
|
+
/**
|
|
1854
|
+
* Compute-config context for app management commands: the project directory
|
|
1855
|
+
* (where `.prisma/local.json` lives) and the config-selected app name, which
|
|
1856
|
+
* ranks below `--app` but above the remembered app selection.
|
|
1857
|
+
*/
|
|
1858
|
+
async function resolveComputeManagementContext(context, configTarget, commandName) {
|
|
1859
|
+
const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
|
|
1860
|
+
return {
|
|
1861
|
+
projectDir: compute.config?.configDir ?? context.runtime.cwd,
|
|
1862
|
+
configAppName: compute.target?.name ?? compute.target?.key ?? void 0
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
async function resolveComputeAppDir(context, compute) {
|
|
1866
|
+
if (!compute.config || !compute.target) return context.runtime.cwd;
|
|
1867
|
+
const appDir = computeTargetAppDir(compute.config, compute.target);
|
|
1868
|
+
if (!compute.target.root) return appDir;
|
|
1869
|
+
context.runtime.signal.throwIfAborted();
|
|
1461
1870
|
try {
|
|
1462
|
-
await access(
|
|
1463
|
-
|
|
1464
|
-
} catch {
|
|
1465
|
-
|
|
1871
|
+
await access(appDir);
|
|
1872
|
+
context.runtime.signal.throwIfAborted();
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
if (context.runtime.signal.aborted) throw error;
|
|
1875
|
+
throw new CliError({
|
|
1876
|
+
code: "COMPUTE_CONFIG_INVALID",
|
|
1877
|
+
domain: "app",
|
|
1878
|
+
summary: `App root "${compute.target.root}" does not exist`,
|
|
1879
|
+
why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
|
|
1880
|
+
fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
|
|
1881
|
+
where: appDir,
|
|
1882
|
+
meta: {
|
|
1883
|
+
appRoot: compute.target.root,
|
|
1884
|
+
appDir
|
|
1885
|
+
},
|
|
1886
|
+
exitCode: 2,
|
|
1887
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
return appDir;
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* `prisma.app.json` is no longer read or written. A leftover file that
|
|
1894
|
+
* matches the effective settings only warns; one with custom values fails
|
|
1895
|
+
* with migration guidance so builds never silently change.
|
|
1896
|
+
*/
|
|
1897
|
+
async function handleLegacyBuildSettings(context, appDir, effective) {
|
|
1898
|
+
const legacy = await detectLegacyBuildSettings({
|
|
1899
|
+
appPath: appDir,
|
|
1900
|
+
effective,
|
|
1901
|
+
signal: context.runtime.signal
|
|
1902
|
+
});
|
|
1903
|
+
switch (legacy.kind) {
|
|
1904
|
+
case "absent": return [];
|
|
1905
|
+
case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
|
|
1906
|
+
case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
|
|
1907
|
+
case "custom": {
|
|
1908
|
+
const buildBlock = [
|
|
1909
|
+
"build: {",
|
|
1910
|
+
` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
|
|
1911
|
+
` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
|
|
1912
|
+
"}"
|
|
1913
|
+
].join(" ");
|
|
1914
|
+
throw new CliError({
|
|
1915
|
+
code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
|
|
1916
|
+
domain: "app",
|
|
1917
|
+
summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
|
|
1918
|
+
why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
|
|
1919
|
+
fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
|
|
1920
|
+
where: legacy.configPath,
|
|
1921
|
+
meta: {
|
|
1922
|
+
configPath: legacy.configPath,
|
|
1923
|
+
buildCommand: legacy.buildCommand,
|
|
1924
|
+
outputDirectory: legacy.outputDirectory
|
|
1925
|
+
},
|
|
1926
|
+
exitCode: 2,
|
|
1927
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1466
1930
|
}
|
|
1467
1931
|
}
|
|
1468
1932
|
async function resolveDeployFramework(context, options) {
|
|
1469
|
-
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1933
|
+
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
|
|
1470
1934
|
if (options.entrypoint) return {
|
|
1471
1935
|
key: "bun",
|
|
1472
1936
|
buildType: "bun",
|
|
1473
1937
|
displayName: "Bun",
|
|
1474
|
-
annotation: "set by --entry"
|
|
1938
|
+
annotation: options.entrypointAnnotation ?? "set by --entry"
|
|
1475
1939
|
};
|
|
1476
|
-
const detected = await detectDeployFramework(context.runtime.
|
|
1940
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
1477
1941
|
if (detected) return detected;
|
|
1478
|
-
throw frameworkNotDetectedError(
|
|
1942
|
+
throw frameworkNotDetectedError(options.appDir);
|
|
1479
1943
|
}
|
|
1480
|
-
function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
1944
|
+
function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
|
|
1481
1945
|
if (requestedHttpPort) return {
|
|
1482
1946
|
port: parseDeployHttpPort(requestedHttpPort),
|
|
1483
|
-
annotation: "set by --http-port"
|
|
1947
|
+
annotation: requestedHttpPortAnnotation ?? "set by --http-port"
|
|
1484
1948
|
};
|
|
1485
1949
|
return {
|
|
1486
1950
|
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
@@ -1491,65 +1955,62 @@ function assertSupportedEntrypointForRequestedDeployShape(options) {
|
|
|
1491
1955
|
if (!options.requestedFramework) return;
|
|
1492
1956
|
assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
|
|
1493
1957
|
}
|
|
1494
|
-
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
|
|
1958
|
+
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signal) {
|
|
1495
1959
|
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1496
|
-
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
|
|
1960
|
+
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
1497
1961
|
if (packageEntrypoint) return packageEntrypoint;
|
|
1498
|
-
|
|
1499
|
-
|
|
1962
|
+
const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
|
|
1963
|
+
if (!defaultEntrypoint) return;
|
|
1964
|
+
signal.throwIfAborted();
|
|
1500
1965
|
try {
|
|
1501
1966
|
await access(path.join(cwd, defaultEntrypoint));
|
|
1967
|
+
signal.throwIfAborted();
|
|
1502
1968
|
return defaultEntrypoint;
|
|
1503
1969
|
} catch (error) {
|
|
1970
|
+
if (signal.aborted) throw error;
|
|
1504
1971
|
if (error.code !== "ENOENT") throw error;
|
|
1505
1972
|
return;
|
|
1506
1973
|
}
|
|
1507
1974
|
}
|
|
1508
|
-
async function detectDeployFramework(cwd) {
|
|
1509
|
-
const packageJson = await readBunPackageJson(cwd);
|
|
1510
|
-
const
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
};
|
|
1523
|
-
if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
|
|
1524
|
-
key: "tanstack-start",
|
|
1525
|
-
buildType: "tanstack-start",
|
|
1526
|
-
displayName: "TanStack Start",
|
|
1527
|
-
annotation: "detected from package.json"
|
|
1528
|
-
};
|
|
1975
|
+
async function detectDeployFramework(cwd, signal) {
|
|
1976
|
+
const packageJson = await readBunPackageJson(cwd, signal);
|
|
1977
|
+
for (const framework of FRAMEWORKS) {
|
|
1978
|
+
if (framework.detectConfigFiles.length === 0 && framework.detectPackages.length === 0) continue;
|
|
1979
|
+
const configFile = await detectFrameworkConfigFile(cwd, framework, signal);
|
|
1980
|
+
if (!configFile.exists && !hasAnyPackageDependency(packageJson, framework.detectPackages)) continue;
|
|
1981
|
+
const annotation = framework.key === "nextjs" && configFile.standalone ? "standalone output detected" : configFile.exists ? `detected from ${path.basename(configFile.path)}` : "detected from package.json";
|
|
1982
|
+
return {
|
|
1983
|
+
key: framework.key,
|
|
1984
|
+
buildType: framework.buildType,
|
|
1985
|
+
displayName: framework.displayName,
|
|
1986
|
+
annotation
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1529
1989
|
return null;
|
|
1530
1990
|
}
|
|
1531
|
-
async function
|
|
1532
|
-
for (const candidate of
|
|
1533
|
-
"next.config.js",
|
|
1534
|
-
"next.config.mjs",
|
|
1535
|
-
"next.config.cjs",
|
|
1536
|
-
"next.config.ts",
|
|
1537
|
-
"next.config.mts"
|
|
1538
|
-
]) {
|
|
1991
|
+
async function detectFrameworkConfigFile(cwd, framework, signal) {
|
|
1992
|
+
for (const candidate of framework.detectConfigFiles) {
|
|
1539
1993
|
const filePath = path.join(cwd, candidate);
|
|
1994
|
+
signal.throwIfAborted();
|
|
1540
1995
|
try {
|
|
1541
|
-
const content = await readFile(filePath,
|
|
1996
|
+
const content = await readFile(filePath, {
|
|
1997
|
+
encoding: "utf8",
|
|
1998
|
+
signal
|
|
1999
|
+
});
|
|
1542
2000
|
return {
|
|
1543
2001
|
exists: true,
|
|
1544
|
-
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
2002
|
+
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content),
|
|
2003
|
+
path: filePath
|
|
1545
2004
|
};
|
|
1546
2005
|
} catch (error) {
|
|
2006
|
+
if (signal.aborted) throw error;
|
|
1547
2007
|
if (error.code !== "ENOENT") throw error;
|
|
1548
2008
|
}
|
|
1549
2009
|
}
|
|
1550
2010
|
return {
|
|
1551
2011
|
exists: false,
|
|
1552
|
-
standalone: false
|
|
2012
|
+
standalone: false,
|
|
2013
|
+
path: null
|
|
1553
2014
|
};
|
|
1554
2015
|
}
|
|
1555
2016
|
function hasPackageDependency(packageJson, dependencyName) {
|
|
@@ -1562,48 +2023,36 @@ function hasDependency(dependencies, dependencyName) {
|
|
|
1562
2023
|
return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
|
|
1563
2024
|
}
|
|
1564
2025
|
function frameworkFromUserFacingValue(value, annotation) {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
};
|
|
1586
|
-
case "tanstack":
|
|
1587
|
-
case "tanstack-start":
|
|
1588
|
-
case "@tanstack/react-start":
|
|
1589
|
-
case "@tanstack/solid-start": return {
|
|
1590
|
-
key: "tanstack-start",
|
|
1591
|
-
buildType: "tanstack-start",
|
|
1592
|
-
displayName: "TanStack Start",
|
|
1593
|
-
annotation
|
|
1594
|
-
};
|
|
1595
|
-
default: throw frameworkNotDetectedError(void 0, value);
|
|
1596
|
-
}
|
|
2026
|
+
const framework = frameworkFromAlias(value);
|
|
2027
|
+
if (!framework) throw frameworkNotDetectedError(void 0, value);
|
|
2028
|
+
return {
|
|
2029
|
+
key: framework.key,
|
|
2030
|
+
buildType: framework.buildType,
|
|
2031
|
+
displayName: framework.displayName,
|
|
2032
|
+
annotation
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
function assertConfigBackedBuildSettings(buildType) {
|
|
2036
|
+
if (isConfigBackedBuildType(buildType)) return;
|
|
2037
|
+
const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
|
|
2038
|
+
throw new CliError({
|
|
2039
|
+
code: "BUILD_SETTINGS_UNSUPPORTED",
|
|
2040
|
+
domain: "app",
|
|
2041
|
+
summary: `build settings are not supported for ${displayName} apps`,
|
|
2042
|
+
why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
|
|
2043
|
+
fix: "Remove the `build` block from prisma.compute.ts for this app.",
|
|
2044
|
+
exitCode: 2
|
|
2045
|
+
});
|
|
1597
2046
|
}
|
|
1598
2047
|
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1599
|
-
const supported =
|
|
2048
|
+
const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
|
|
1600
2049
|
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1601
2050
|
return new CliError({
|
|
1602
2051
|
code: "FRAMEWORK_NOT_DETECTED",
|
|
1603
2052
|
domain: "app",
|
|
1604
2053
|
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1605
2054
|
why: `Supported Beta frameworks: ${supported}.`,
|
|
1606
|
-
fix:
|
|
2055
|
+
fix: `Add one of these frameworks as a dependency, pass --framework <${FRAMEWORKS.map((framework) => framework.key).join("|")}>, or pass --entry <path> for a Bun app.`,
|
|
1607
2056
|
exitCode: 2,
|
|
1608
2057
|
nextSteps: [
|
|
1609
2058
|
"prisma-cli app deploy --framework nextjs",
|
|
@@ -1616,14 +2065,72 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
|
1616
2065
|
}
|
|
1617
2066
|
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1618
2067
|
if (context.flags.json || context.flags.quiet) return;
|
|
1619
|
-
const directory =
|
|
2068
|
+
const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
|
|
1620
2069
|
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1621
2070
|
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1622
2071
|
}
|
|
2072
|
+
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
2073
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
2074
|
+
const settings = resolution.settings;
|
|
2075
|
+
const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
|
|
2076
|
+
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [
|
|
2077
|
+
{
|
|
2078
|
+
label: "Build Command",
|
|
2079
|
+
value: settings.buildCommand ?? "none",
|
|
2080
|
+
origin: settings.buildCommandSource ?? void 0
|
|
2081
|
+
},
|
|
2082
|
+
{
|
|
2083
|
+
label: "Output Directory",
|
|
2084
|
+
value: settings.outputDirectory,
|
|
2085
|
+
origin: settings.outputDirectorySource ?? void 0
|
|
2086
|
+
},
|
|
2087
|
+
...settings.entrypoint ? [{
|
|
2088
|
+
label: "Entrypoint",
|
|
2089
|
+
value: settings.entrypoint,
|
|
2090
|
+
origin: settings.entrypointSource ?? void 0
|
|
2091
|
+
}] : []
|
|
2092
|
+
]).join("\n")}\n\n`);
|
|
2093
|
+
}
|
|
1623
2094
|
function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
|
|
1624
2095
|
if (context.flags.json || context.flags.quiet) return;
|
|
1625
2096
|
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
1626
2097
|
}
|
|
2098
|
+
async function maybePromptForAgentSetup(context, projectDir) {
|
|
2099
|
+
if (!canPrompt(context) || context.flags.yes) return [];
|
|
2100
|
+
if (!shouldOfferPrismaAgentSetup(await readPrismaAgentSetupStatus({
|
|
2101
|
+
cwd: projectDir,
|
|
2102
|
+
stateStore: context.stateStore,
|
|
2103
|
+
signal: context.runtime.signal,
|
|
2104
|
+
requiredSkill: "prisma-compute"
|
|
2105
|
+
}))) return [];
|
|
2106
|
+
if (!await confirmPrompt({
|
|
2107
|
+
input: context.runtime.stdin,
|
|
2108
|
+
output: context.runtime.stderr,
|
|
2109
|
+
signal: context.runtime.signal,
|
|
2110
|
+
message: "Install the Prisma Compute skill for this project?",
|
|
2111
|
+
initialValue: true
|
|
2112
|
+
})) {
|
|
2113
|
+
await context.stateStore.setAgentSetupPromptDismissedAt((/* @__PURE__ */ new Date()).toISOString());
|
|
2114
|
+
return [];
|
|
2115
|
+
}
|
|
2116
|
+
try {
|
|
2117
|
+
await runAgentInstall(context, { skill: [PRISMA_COMPUTE_AGENT_SKILL] }, "install", { cwd: projectDir });
|
|
2118
|
+
if (!context.flags.quiet && !context.flags.json) context.output.stderr.write(`${renderSummaryLine(context.ui, "success", "Installed the Prisma Compute skill for this project.")}\n\n`);
|
|
2119
|
+
return [];
|
|
2120
|
+
} catch (error) {
|
|
2121
|
+
const message = error instanceof Error ? error.message : "Prisma skill install failed.";
|
|
2122
|
+
if (!context.flags.quiet && !context.flags.json) context.output.stderr.write(`${renderSummaryLine(context.ui, "warning", `Skipped Prisma skill install: ${message}`)}\n\n`);
|
|
2123
|
+
return [`The Prisma Compute skill was not installed. Run ${await resolvePrismaCliPackageCommand({
|
|
2124
|
+
cwd: projectDir,
|
|
2125
|
+
signal: context.runtime.signal,
|
|
2126
|
+
args: [
|
|
2127
|
+
...PRISMA_AGENT_INSTALL_ARGS,
|
|
2128
|
+
"--skill",
|
|
2129
|
+
PRISMA_COMPUTE_AGENT_SKILL
|
|
2130
|
+
]
|
|
2131
|
+
})} to try again.`];
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
1627
2134
|
async function maybeCustomizeDeploySettings(context, options) {
|
|
1628
2135
|
if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitEntrypoint || options.explicitHttpPort || !canPrompt(context)) return {
|
|
1629
2136
|
framework: options.framework,
|
|
@@ -1636,6 +2143,7 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1636
2143
|
if (!await confirmPrompt({
|
|
1637
2144
|
input: context.runtime.stdin,
|
|
1638
2145
|
output: context.runtime.stderr,
|
|
2146
|
+
signal: context.runtime.signal,
|
|
1639
2147
|
message: "Customize build settings?",
|
|
1640
2148
|
initialValue: false
|
|
1641
2149
|
})) return {
|
|
@@ -1645,15 +2153,17 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1645
2153
|
const framework = frameworkFromUserFacingValue(await selectPrompt({
|
|
1646
2154
|
input: context.runtime.stdin,
|
|
1647
2155
|
output: context.runtime.stderr,
|
|
2156
|
+
signal: context.runtime.signal,
|
|
1648
2157
|
message: `Framework (${options.framework.displayName})`,
|
|
1649
|
-
choices:
|
|
1650
|
-
label:
|
|
1651
|
-
value: framework
|
|
2158
|
+
choices: FRAMEWORKS.map((framework) => ({
|
|
2159
|
+
label: framework.displayName,
|
|
2160
|
+
value: framework.key
|
|
1652
2161
|
}))
|
|
1653
2162
|
}), "set by you");
|
|
1654
2163
|
const requestedPort = await textPrompt({
|
|
1655
2164
|
input: context.runtime.stdin,
|
|
1656
2165
|
output: context.runtime.stderr,
|
|
2166
|
+
signal: context.runtime.signal,
|
|
1657
2167
|
message: `HTTP port (${options.runtime.port})`,
|
|
1658
2168
|
placeholder: String(options.runtime.port),
|
|
1659
2169
|
validate: validateDeployHttpPortText
|
|
@@ -1691,14 +2201,6 @@ function maybeRenderDeploySettingsPreview(context, options) {
|
|
|
1691
2201
|
value: `HTTP ${options.runtime.port}`
|
|
1692
2202
|
}]).join("\n")}\n\n`);
|
|
1693
2203
|
}
|
|
1694
|
-
function frameworkDisplayName(framework) {
|
|
1695
|
-
switch (framework) {
|
|
1696
|
-
case "nextjs": return "Next.js";
|
|
1697
|
-
case "hono": return "Hono";
|
|
1698
|
-
case "tanstack-start": return "TanStack Start";
|
|
1699
|
-
case "bun": return "Bun";
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
2204
|
function validateDeployHttpPortText(value) {
|
|
1703
2205
|
if (!value?.trim()) return;
|
|
1704
2206
|
try {
|
|
@@ -1712,34 +2214,49 @@ function formatDeployDirectory(cwd) {
|
|
|
1712
2214
|
const basename = path.basename(cwd);
|
|
1713
2215
|
return basename ? `./${basename}` : ".";
|
|
1714
2216
|
}
|
|
2217
|
+
function formatAppDirectoryLabel(cwd, appDir) {
|
|
2218
|
+
if (appDir === cwd) return formatDeployDirectory(cwd);
|
|
2219
|
+
const relative = path.relative(cwd, appDir).split(path.sep).join("/");
|
|
2220
|
+
return relative.startsWith("..") ? relative : `./${relative}`;
|
|
2221
|
+
}
|
|
1715
2222
|
async function readCurrentWorkspaceId(context) {
|
|
1716
2223
|
const state = await context.stateStore.read();
|
|
1717
2224
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
1718
|
-
return (await readAuthState(context.runtime.env)).workspace?.id ?? null;
|
|
2225
|
+
return (await readAuthState(context.runtime.env, context.runtime.signal)).workspace?.id ?? null;
|
|
1719
2226
|
}
|
|
1720
2227
|
function normalizeBuildType(requestedBuildType) {
|
|
1721
2228
|
if (!requestedBuildType) return "auto";
|
|
1722
2229
|
if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
|
|
1723
|
-
throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${
|
|
2230
|
+
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");
|
|
1724
2231
|
}
|
|
1725
2232
|
function isPreviewBuildType(value) {
|
|
1726
|
-
return
|
|
2233
|
+
return APP_BUILD_TYPES.includes(value);
|
|
1727
2234
|
}
|
|
1728
2235
|
function getBuildTypeExamples(commandName) {
|
|
1729
|
-
return
|
|
2236
|
+
return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
|
|
1730
2237
|
return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
|
|
1731
2238
|
});
|
|
1732
2239
|
}
|
|
1733
2240
|
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
1734
|
-
if (buildType !== "auto" && buildType
|
|
2241
|
+
if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
|
|
1735
2242
|
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");
|
|
1736
2243
|
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");
|
|
1737
2244
|
}
|
|
1738
2245
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
2246
|
+
/**
|
|
2247
|
+
* Resolves the framework for `app run` with the same detection as deploy, so
|
|
2248
|
+
* a repo that deploys without flags also runs without flags. Local dev server
|
|
2249
|
+
* support is intentionally narrower than deploy build support: only Next.js
|
|
2250
|
+
* and Bun/Hono have dev servers in the current preview.
|
|
2251
|
+
*/
|
|
2252
|
+
async function resolveLocalRunFramework(context, options) {
|
|
2253
|
+
if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
|
|
2254
|
+
if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME$1}`);
|
|
2255
|
+
return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
|
|
2256
|
+
}
|
|
2257
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
2258
|
+
if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
|
|
2259
|
+
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");
|
|
1743
2260
|
}
|
|
1744
2261
|
function parseLocalPort(requestedPort) {
|
|
1745
2262
|
if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
|
|
@@ -1756,6 +2273,19 @@ function parseDeployHttpPort(requestedPort) {
|
|
|
1756
2273
|
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");
|
|
1757
2274
|
return port;
|
|
1758
2275
|
}
|
|
2276
|
+
function parseDeployRegion(requestedRegion, source) {
|
|
2277
|
+
const region = requestedRegion.trim();
|
|
2278
|
+
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");
|
|
2279
|
+
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");
|
|
2280
|
+
return region;
|
|
2281
|
+
}
|
|
2282
|
+
function normalizeDeployRegionInput(region) {
|
|
2283
|
+
if (!region) return;
|
|
2284
|
+
return {
|
|
2285
|
+
...region,
|
|
2286
|
+
value: parseDeployRegion(region.value, region.annotation)
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
1759
2289
|
function ensurePreviewAppMode(context) {
|
|
1760
2290
|
if (isRealMode(context)) return;
|
|
1761
2291
|
throw featureUnavailableError("App commands are not available in fixture mode", "Preview app commands require live app deployment integration.", "Rerun without fixture mode enabled to use preview app deployment workflows.", ["prisma-cli auth login", "prisma-cli project show"], "app");
|
|
@@ -1812,7 +2342,7 @@ function appDeployFailedError(error, progress) {
|
|
|
1812
2342
|
}
|
|
1813
2343
|
if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
|
|
1814
2344
|
const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
|
|
1815
|
-
const recoveryLines = progress.
|
|
2345
|
+
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."];
|
|
1816
2346
|
const urlLines = progress.deploymentUrl ? [
|
|
1817
2347
|
"",
|
|
1818
2348
|
"URL",
|
|
@@ -1838,11 +2368,11 @@ function appDeployFailedError(error, progress) {
|
|
|
1838
2368
|
domain: "app",
|
|
1839
2369
|
summary: phaseHeadline,
|
|
1840
2370
|
why,
|
|
1841
|
-
fix: progress.
|
|
2371
|
+
fix: progress.deploymentId ? `Inspect logs with prisma-cli app logs --deployment ${progress.deploymentId}.` : "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
1842
2372
|
debug,
|
|
1843
2373
|
meta: {
|
|
1844
2374
|
phase: progress.containerLive ? "runtime_ready" : "deploy",
|
|
1845
|
-
deploymentId: progress.
|
|
2375
|
+
deploymentId: progress.deploymentId,
|
|
1846
2376
|
deploymentUrl: progress.deploymentUrl
|
|
1847
2377
|
},
|
|
1848
2378
|
humanLines,
|
|
@@ -1866,6 +2396,18 @@ function localResolutionPinStaleError() {
|
|
|
1866
2396
|
]
|
|
1867
2397
|
});
|
|
1868
2398
|
}
|
|
2399
|
+
function localPinReadErrorToDeployError(error) {
|
|
2400
|
+
return matchError(error, {
|
|
2401
|
+
LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
|
|
2402
|
+
LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
|
|
2403
|
+
LocalResolutionPinReadAbortedError: (error) => {
|
|
2404
|
+
throw error;
|
|
2405
|
+
},
|
|
2406
|
+
UnhandledException: (error) => {
|
|
2407
|
+
throw error;
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
1869
2411
|
function readDeployEnvOverride(context, name) {
|
|
1870
2412
|
const value = context.runtime.env[name]?.trim();
|
|
1871
2413
|
return value ? value : void 0;
|
|
@@ -1944,14 +2486,12 @@ function isAutoBuildDetectionError(error) {
|
|
|
1944
2486
|
return error instanceof Error && error.message.startsWith("Entrypoint is required.");
|
|
1945
2487
|
}
|
|
1946
2488
|
function formatBuildTypeName(buildType) {
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
case "tanstack-start": return "TanStack Start";
|
|
1952
|
-
case "bun": return "Bun";
|
|
1953
|
-
case "auto": return "Auto";
|
|
2489
|
+
if (buildType === "auto") return "Auto";
|
|
2490
|
+
for (let index = FRAMEWORKS.length - 1; index >= 0; index -= 1) {
|
|
2491
|
+
const framework = FRAMEWORKS[index];
|
|
2492
|
+
if (framework?.buildType === buildType) return framework.displayName;
|
|
1954
2493
|
}
|
|
2494
|
+
return buildType;
|
|
1955
2495
|
}
|
|
1956
2496
|
function removeFailedError(summary, error, nextSteps) {
|
|
1957
2497
|
return new CliError({
|