@prisma/cli 3.0.0-beta.3 → 3.0.0-beta.30
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 +6 -15
- package/dist/adapters/local-state.js +15 -4
- package/dist/adapters/mock-api.js +244 -0
- package/dist/adapters/token-storage.js +335 -34
- package/dist/cli.js +7 -7
- package/dist/cli2.js +24 -5
- package/dist/commands/agent/index.js +60 -0
- package/dist/commands/app/index.js +91 -61
- package/dist/commands/auth/index.js +55 -2
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/bucket/index.js +123 -0
- package/dist/commands/build/index.js +29 -0
- package/dist/commands/database/index.js +249 -0
- package/dist/commands/env.js +8 -4
- package/dist/commands/feedback/index.js +20 -0
- package/dist/commands/git/index.js +1 -1
- package/dist/commands/init/index.js +33 -0
- package/dist/commands/project/index.js +54 -5
- package/dist/controllers/agent-setup.js +52 -0
- package/dist/controllers/agent.js +228 -0
- package/dist/controllers/app-env-api.js +55 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +227 -104
- package/dist/controllers/app.js +746 -306
- package/dist/controllers/auth.js +247 -3
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/bucket.js +278 -0
- package/dist/controllers/build.js +88 -0
- package/dist/controllers/database.js +567 -0
- package/dist/controllers/feedback.js +86 -0
- package/dist/controllers/init.js +753 -0
- package/dist/controllers/project.js +377 -22
- package/dist/controllers/select-prompt-port.js +1 -0
- package/dist/lib/agent/cli-command.js +20 -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/{preview-provider.js → app-provider.js} +137 -88
- 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 +83 -0
- package/dist/lib/app/bun-project.js +3 -4
- 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 +3 -60
- 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 +10 -4
- package/dist/lib/auth/guard.js +4 -1
- package/dist/lib/auth/login.js +33 -26
- package/dist/lib/auth/recipient.js +42 -0
- package/dist/lib/bucket/provider.js +139 -0
- package/dist/lib/database/provider.js +378 -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 +5 -4
- package/dist/lib/project/local-pin.js +171 -41
- package/dist/lib/project/provider.js +92 -0
- package/dist/lib/project/resolution.js +199 -48
- package/dist/lib/project/setup.js +67 -20
- 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 +208 -27
- package/dist/presenters/auth.js +99 -2
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/bucket.js +174 -0
- package/dist/presenters/database.js +448 -0
- package/dist/presenters/feedback.js +26 -0
- package/dist/presenters/init.js +30 -0
- package/dist/presenters/project.js +139 -27
- 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 +458 -17
- package/dist/shell/command-runner.js +58 -18
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +56 -1
- package/dist/shell/help.js +31 -20
- package/dist/shell/output.js +72 -1
- package/dist/shell/prompt.js +12 -5
- package/dist/shell/runtime.js +8 -4
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +2 -2
- 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/dist/use-cases/project.js +2 -1
- package/package.json +21 -4
- package/dist/lib/app/preview-build.js +0 -312
- package/dist/lib/app/preview-interaction.js +0 -5
package/dist/controllers/app.js
CHANGED
|
@@ -1,54 +1,81 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
1
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
3
2
|
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
4
3
|
import { renderCommandHeader } from "../shell/ui.js";
|
|
5
|
-
import { writeJsonEvent } from "../shell/output.js";
|
|
6
4
|
import { canPrompt } from "../shell/runtime.js";
|
|
5
|
+
import { writeJsonEvent } from "../shell/output.js";
|
|
6
|
+
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
7
|
+
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
8
|
+
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
|
|
9
|
+
import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
|
|
10
|
+
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
11
|
+
import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
|
|
7
12
|
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
13
|
+
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
14
|
+
import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
15
|
+
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
16
|
+
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
17
|
+
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
18
|
+
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";
|
|
19
|
+
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
20
|
+
import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
|
|
21
|
+
import { createDeployProgress, createDeployProgressState, createPromoteProgress } from "../lib/app/deploy-progress.js";
|
|
22
|
+
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
23
|
+
import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
|
|
24
|
+
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
25
|
+
import { resolveReadBranch } from "../lib/app/read-branch.js";
|
|
8
26
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
27
|
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";
|
|
28
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
18
29
|
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
19
|
-
import {
|
|
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";
|
|
30
|
+
import { maybePromptForAgentSetup } from "./agent-setup.js";
|
|
24
31
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
25
32
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
26
33
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
27
|
-
import { access, readFile } from "node:fs/promises";
|
|
28
34
|
import path from "node:path";
|
|
35
|
+
import { access } from "node:fs/promises";
|
|
36
|
+
import { COMPUTE_REGIONS, ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, frameworkByKey, frameworkFromAlias, isConfigBackedBuildType } from "@prisma/compute-sdk/config";
|
|
37
|
+
import { detectComputeAppFromDirectory } from "@prisma/compute-sdk/config/directory";
|
|
38
|
+
import { Result, matchError } from "better-result";
|
|
29
39
|
import open from "open";
|
|
30
40
|
//#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
41
|
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
39
42
|
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
40
43
|
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
44
|
+
const COMPUTE_REGION_IDS = new Set(COMPUTE_REGIONS);
|
|
41
45
|
function isRealMode(context) {
|
|
42
46
|
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
43
47
|
}
|
|
44
|
-
async function runAppBuild(context,
|
|
45
|
-
const
|
|
46
|
-
|
|
48
|
+
async function runAppBuild(context, options) {
|
|
49
|
+
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
|
|
50
|
+
const merged = mergeComputeLocalInputs({
|
|
51
|
+
cli: {
|
|
52
|
+
entrypoint: options?.entrypoint,
|
|
53
|
+
buildType: options?.buildType
|
|
54
|
+
},
|
|
55
|
+
target: compute.target
|
|
56
|
+
});
|
|
57
|
+
const appDir = await resolveComputeAppDir(context, compute);
|
|
58
|
+
let buildType = normalizeBuildType(merged.buildType);
|
|
59
|
+
if (compute.target?.build && buildType === "auto") {
|
|
60
|
+
const detected = await detectDeployFramework(appDir, context.runtime.signal);
|
|
61
|
+
if (!detected) throw frameworkNotDetectedError(appDir);
|
|
62
|
+
buildType = detected.buildType;
|
|
63
|
+
}
|
|
64
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
|
|
65
|
+
if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
|
|
66
|
+
const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
|
|
67
|
+
appPath: appDir,
|
|
68
|
+
buildType,
|
|
69
|
+
configured: compute.target.build,
|
|
70
|
+
configPath: compute.config.configPath,
|
|
71
|
+
signal: context.runtime.signal
|
|
72
|
+
})).settings : void 0;
|
|
47
73
|
try {
|
|
48
|
-
const { artifact, buildType: actualBuildType } = await
|
|
49
|
-
appPath:
|
|
50
|
-
entrypoint,
|
|
74
|
+
const { artifact, buildType: actualBuildType } = await executeAppBuild({
|
|
75
|
+
appPath: appDir,
|
|
76
|
+
entrypoint: merged.entrypoint,
|
|
51
77
|
buildType,
|
|
78
|
+
buildSettings,
|
|
52
79
|
signal: context.runtime.signal
|
|
53
80
|
});
|
|
54
81
|
return {
|
|
@@ -62,21 +89,38 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
|
|
|
62
89
|
nextSteps: ["prisma-cli app deploy"]
|
|
63
90
|
};
|
|
64
91
|
} catch (error) {
|
|
65
|
-
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 ${
|
|
92
|
+
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");
|
|
66
93
|
throw buildFailedError("Local app build failed", error);
|
|
67
94
|
}
|
|
68
95
|
}
|
|
69
|
-
async function runAppRun(context,
|
|
96
|
+
async function runAppRun(context, options) {
|
|
70
97
|
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");
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
|
|
99
|
+
const merged = mergeComputeLocalInputs({
|
|
100
|
+
cli: {
|
|
101
|
+
entrypoint: options?.entrypoint,
|
|
102
|
+
buildType: options?.buildType,
|
|
103
|
+
port: options?.port
|
|
104
|
+
},
|
|
105
|
+
target: compute.target
|
|
106
|
+
});
|
|
107
|
+
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");
|
|
108
|
+
const appDir = await resolveComputeAppDir(context, compute);
|
|
109
|
+
const buildType = normalizeBuildType(merged.buildType);
|
|
110
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
|
|
111
|
+
const port = parseLocalPort(merged.port);
|
|
112
|
+
const framework = await resolveLocalRunFramework(context, {
|
|
113
|
+
requestedBuildType: buildType,
|
|
114
|
+
configFramework: compute.target?.framework ?? null,
|
|
115
|
+
appDir,
|
|
116
|
+
entrypoint: merged.entrypoint
|
|
117
|
+
});
|
|
118
|
+
const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
|
|
75
119
|
let runResult;
|
|
76
120
|
try {
|
|
77
121
|
runResult = await runLocalApp({
|
|
78
|
-
appPath:
|
|
79
|
-
buildType:
|
|
122
|
+
appPath: appDir,
|
|
123
|
+
buildType: framework.buildType,
|
|
80
124
|
entrypoint,
|
|
81
125
|
port,
|
|
82
126
|
env: context.runtime.env,
|
|
@@ -101,6 +145,107 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
|
|
|
101
145
|
}
|
|
102
146
|
async function runAppDeploy(context, appName, options) {
|
|
103
147
|
ensurePreviewAppMode(context);
|
|
148
|
+
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
149
|
+
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
|
|
150
|
+
const config = loaded.value;
|
|
151
|
+
const plan = planAppDeploy({
|
|
152
|
+
config,
|
|
153
|
+
requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
|
|
154
|
+
hasCreateProject: options?.createProjectName !== void 0
|
|
155
|
+
});
|
|
156
|
+
if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
|
|
157
|
+
return runSingleAppDeploy(context, appName, options, config);
|
|
158
|
+
}
|
|
159
|
+
async function runAppDeployAll(context, config, plannedTargets, appName, options) {
|
|
160
|
+
assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options);
|
|
161
|
+
const deployments = [];
|
|
162
|
+
const warnings = [];
|
|
163
|
+
for (const planned of plannedTargets) {
|
|
164
|
+
maybeRenderDeployAllTargetHeader(context, planned);
|
|
165
|
+
const targetOptions = {
|
|
166
|
+
...options,
|
|
167
|
+
configTarget: planned.targetKey,
|
|
168
|
+
createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
|
|
169
|
+
};
|
|
170
|
+
try {
|
|
171
|
+
const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
|
|
172
|
+
deployments.push({
|
|
173
|
+
target: planned.targetKey,
|
|
174
|
+
result: single.result
|
|
175
|
+
});
|
|
176
|
+
warnings.push(...single.warnings);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
throw deployAllFailedError(error, plannedTargets, planned.index, deployments);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
command: "app.deploy",
|
|
183
|
+
result: { deployments },
|
|
184
|
+
warnings,
|
|
185
|
+
nextSteps: ["prisma-cli app list-deploys <app>"]
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options) {
|
|
189
|
+
const used = perAppInputsForDeployAll({
|
|
190
|
+
appName,
|
|
191
|
+
framework: options?.framework,
|
|
192
|
+
entrypoint: options?.entrypoint,
|
|
193
|
+
httpPort: options?.httpPort,
|
|
194
|
+
region: options?.region,
|
|
195
|
+
envAssignments: options?.envAssignments,
|
|
196
|
+
appIdEnvVar: {
|
|
197
|
+
name: PRISMA_APP_ID_ENV_VAR,
|
|
198
|
+
value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
if (used.length === 0) return;
|
|
202
|
+
const targetKeys = plannedTargets.map((target) => target.targetKey);
|
|
203
|
+
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");
|
|
204
|
+
}
|
|
205
|
+
function maybeRenderDeployAllTargetHeader(context, planned) {
|
|
206
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
207
|
+
context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
|
|
208
|
+
}
|
|
209
|
+
function deployAllFailedError(error, plannedTargets, failedIndex, deployments) {
|
|
210
|
+
if (!(error instanceof CliError)) return error;
|
|
211
|
+
const failure = describeDeployAllFailure({
|
|
212
|
+
targetKeys: plannedTargets.map((target) => target.targetKey),
|
|
213
|
+
failedIndex,
|
|
214
|
+
completed: deployments.map(({ target, result }) => ({
|
|
215
|
+
target,
|
|
216
|
+
deploymentId: result.deployment.id,
|
|
217
|
+
url: result.deployment.url
|
|
218
|
+
}))
|
|
219
|
+
});
|
|
220
|
+
const contextSentence = failure.contextLines.join(" ");
|
|
221
|
+
return new CliError({
|
|
222
|
+
code: error.code,
|
|
223
|
+
domain: error.domain,
|
|
224
|
+
summary: error.summary,
|
|
225
|
+
why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
|
|
226
|
+
fix: error.fix,
|
|
227
|
+
debug: error.debug,
|
|
228
|
+
where: error.where,
|
|
229
|
+
meta: {
|
|
230
|
+
...error.meta,
|
|
231
|
+
deployAll: {
|
|
232
|
+
failedTarget: failure.failedTarget,
|
|
233
|
+
completed: failure.completed,
|
|
234
|
+
notAttempted: failure.notAttempted
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
docsUrl: error.docsUrl,
|
|
238
|
+
exitCode: error.exitCode,
|
|
239
|
+
nextSteps: error.nextSteps,
|
|
240
|
+
nextActions: error.nextActions,
|
|
241
|
+
humanLines: error.humanLines ? [
|
|
242
|
+
...error.humanLines,
|
|
243
|
+
"",
|
|
244
|
+
...failure.contextLines
|
|
245
|
+
] : void 0
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
104
249
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
105
250
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
106
251
|
assertExclusiveDeployProjectInputs({
|
|
@@ -108,42 +253,67 @@ async function runAppDeploy(context, appName, options) {
|
|
|
108
253
|
createProjectName: options?.createProjectName,
|
|
109
254
|
envProjectId
|
|
110
255
|
});
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
256
|
+
const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
|
|
257
|
+
const merged = mergeComputeDeployInputs({
|
|
258
|
+
cli: {
|
|
259
|
+
framework: options?.framework,
|
|
260
|
+
entrypoint: options?.entrypoint,
|
|
261
|
+
httpPort: options?.httpPort,
|
|
262
|
+
region: options?.region,
|
|
263
|
+
envInputs: options?.envAssignments
|
|
264
|
+
},
|
|
265
|
+
target: computeConfig.target,
|
|
266
|
+
configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1
|
|
267
|
+
});
|
|
268
|
+
const appDir = await resolveComputeAppDir(context, computeConfig);
|
|
269
|
+
const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
|
|
270
|
+
const agentSetupWarnings = await maybePromptForAgentSetup(context, projectDir);
|
|
271
|
+
const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(projectDir, context.runtime.signal);
|
|
272
|
+
if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
|
|
273
|
+
const localPin = localPinReadResult.value;
|
|
114
274
|
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
115
|
-
if (
|
|
275
|
+
if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
|
|
276
|
+
const deployRegion = normalizeDeployRegionInput(merged.region);
|
|
116
277
|
assertSupportedEntrypointForRequestedDeployShape({
|
|
117
|
-
requestedFramework:
|
|
118
|
-
entrypoint:
|
|
278
|
+
requestedFramework: merged.framework?.value,
|
|
279
|
+
entrypoint: merged.entrypoint?.value
|
|
119
280
|
});
|
|
120
281
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
121
282
|
branch,
|
|
122
283
|
createProjectName: options?.createProjectName,
|
|
284
|
+
createProjectRegion: deployRegion?.value,
|
|
123
285
|
envProjectId,
|
|
124
286
|
localPin
|
|
125
287
|
});
|
|
126
288
|
let localPinResult;
|
|
127
289
|
if (target.localPinAction) {
|
|
128
|
-
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
129
|
-
|
|
130
|
-
|
|
290
|
+
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
|
|
291
|
+
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
292
|
+
const projectSetup = setupResult.value;
|
|
293
|
+
localPinResult = projectSetup.localPin;
|
|
294
|
+
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
131
295
|
}
|
|
132
296
|
let framework = await resolveDeployFramework(context, {
|
|
133
|
-
requestedFramework:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
297
|
+
requestedFramework: merged.framework?.value,
|
|
298
|
+
requestedFrameworkAnnotation: merged.framework?.annotation,
|
|
299
|
+
entrypoint: merged.entrypoint?.value,
|
|
300
|
+
entrypointAnnotation: merged.entrypoint?.annotation,
|
|
301
|
+
appDir
|
|
302
|
+
});
|
|
303
|
+
let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
|
|
304
|
+
assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
|
|
305
|
+
const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
|
|
139
306
|
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
140
307
|
explicitAppName: appName,
|
|
141
308
|
explicitAppId: envAppId,
|
|
309
|
+
configAppName: merged.configAppName,
|
|
310
|
+
configRegion: deployRegion,
|
|
142
311
|
firstDeploy: Boolean(target.localPinAction),
|
|
143
|
-
inferName: () => inferTargetName(
|
|
312
|
+
inferName: () => inferTargetName(appDir, context.runtime.signal)
|
|
144
313
|
});
|
|
145
314
|
await maybeRenderDeploySetupBlock(context, {
|
|
146
315
|
includeDirectory: !target.localPinAction,
|
|
316
|
+
appDir,
|
|
147
317
|
projectName: target.project.name,
|
|
148
318
|
branchName: target.branch.name,
|
|
149
319
|
appName: selectedApp.displayName
|
|
@@ -152,20 +322,41 @@ async function runAppDeploy(context, appName, options) {
|
|
|
152
322
|
framework,
|
|
153
323
|
runtime,
|
|
154
324
|
firstDeploy: selectedApp.firstDeploy,
|
|
155
|
-
explicitFramework: Boolean(
|
|
156
|
-
explicitEntrypoint: Boolean(
|
|
157
|
-
explicitHttpPort: Boolean(
|
|
325
|
+
explicitFramework: Boolean(merged.framework),
|
|
326
|
+
explicitEntrypoint: Boolean(merged.entrypoint),
|
|
327
|
+
explicitHttpPort: Boolean(merged.httpPort)
|
|
158
328
|
});
|
|
159
329
|
framework = customized.framework;
|
|
160
330
|
runtime = customized.runtime;
|
|
331
|
+
const noPromote = options?.noPromote === true;
|
|
332
|
+
const productionDeployGate = noPromote ? { firstProductionDeploy: false } : await enforceProductionDeployGate(context, provider, {
|
|
333
|
+
appId: selectedApp.appId,
|
|
334
|
+
appName: selectedApp.displayName,
|
|
335
|
+
branchKind: target.branch.kind,
|
|
336
|
+
prod: options?.prod === true
|
|
337
|
+
});
|
|
161
338
|
const buildType = framework.buildType;
|
|
162
|
-
assertSupportedEntrypoint(buildType,
|
|
163
|
-
const entrypoint = await resolveDeployEntrypoint(
|
|
339
|
+
assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
|
|
340
|
+
const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
|
|
341
|
+
const buildSettingsResolution = await resolveDeployBuildSettings({
|
|
342
|
+
computeConfig,
|
|
343
|
+
appDir,
|
|
344
|
+
buildType,
|
|
345
|
+
signal: context.runtime.signal
|
|
346
|
+
});
|
|
347
|
+
const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
|
|
348
|
+
maybeRenderDeployBuildSettings(context, buildSettingsResolution);
|
|
164
349
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
165
|
-
const
|
|
350
|
+
const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
|
|
351
|
+
db: options?.db,
|
|
352
|
+
providedEnvVars: envVars,
|
|
353
|
+
firstProductionDeploy: productionDeployGate.firstProductionDeploy,
|
|
354
|
+
projectDir
|
|
355
|
+
});
|
|
356
|
+
const progressState = createDeployProgressState();
|
|
166
357
|
const deployStartedAt = Date.now();
|
|
167
358
|
const deployResult = await provider.deployApp({
|
|
168
|
-
cwd:
|
|
359
|
+
cwd: appDir,
|
|
169
360
|
projectId,
|
|
170
361
|
branchName: target.branch.name,
|
|
171
362
|
appId: selectedApp.appId,
|
|
@@ -173,11 +364,13 @@ async function runAppDeploy(context, appName, options) {
|
|
|
173
364
|
region: selectedApp.region,
|
|
174
365
|
entrypoint,
|
|
175
366
|
buildType,
|
|
367
|
+
buildSettings: buildSettingsResolution.settings,
|
|
176
368
|
portMapping,
|
|
177
369
|
envVars,
|
|
370
|
+
skipPromote: noPromote,
|
|
178
371
|
interaction: void 0,
|
|
179
372
|
signal: context.runtime.signal,
|
|
180
|
-
progress:
|
|
373
|
+
progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
|
|
181
374
|
}).catch((error) => {
|
|
182
375
|
throw appDeployFailedError(error, progressState);
|
|
183
376
|
});
|
|
@@ -186,34 +379,87 @@ async function runAppDeploy(context, appName, options) {
|
|
|
186
379
|
id: deployResult.app.id,
|
|
187
380
|
name: deployResult.app.name
|
|
188
381
|
});
|
|
189
|
-
|
|
382
|
+
const knownLiveDeploymentId = deployResult.promoted ? deployResult.deployment.id : deployResult.app.liveDeploymentId;
|
|
383
|
+
if (knownLiveDeploymentId) await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, knownLiveDeploymentId);
|
|
190
384
|
return {
|
|
191
385
|
command: "app.deploy",
|
|
192
386
|
result: {
|
|
193
387
|
workspace: target.workspace,
|
|
194
388
|
project: target.project,
|
|
195
|
-
branch: target.branch,
|
|
389
|
+
branch: toResultBranch(target.branch),
|
|
196
390
|
resolution: target.resolution,
|
|
391
|
+
branchDatabase: branchDatabaseSetup.result,
|
|
197
392
|
app: {
|
|
198
393
|
id: deployResult.app.id,
|
|
199
394
|
name: deployResult.app.name
|
|
200
395
|
},
|
|
201
396
|
deployment: deployResult.deployment,
|
|
397
|
+
promoted: deployResult.promoted,
|
|
398
|
+
deploySettings: {
|
|
399
|
+
config: {
|
|
400
|
+
path: computeConfig.config?.relativeConfigPath ?? null,
|
|
401
|
+
status: buildSettingsResolution.status
|
|
402
|
+
},
|
|
403
|
+
buildCommand: {
|
|
404
|
+
value: buildSettingsResolution.settings.buildCommand,
|
|
405
|
+
source: buildSettingsResolution.settings.buildCommandSource
|
|
406
|
+
},
|
|
407
|
+
outputDirectory: {
|
|
408
|
+
value: buildSettingsResolution.settings.outputDirectory,
|
|
409
|
+
source: buildSettingsResolution.settings.outputDirectorySource
|
|
410
|
+
},
|
|
411
|
+
framework: {
|
|
412
|
+
key: framework.key,
|
|
413
|
+
buildType,
|
|
414
|
+
name: framework.displayName,
|
|
415
|
+
source: framework.annotation
|
|
416
|
+
},
|
|
417
|
+
entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
|
|
418
|
+
httpPort: runtime.port,
|
|
419
|
+
region: deployResult.app.region ?? selectedApp.region ?? null,
|
|
420
|
+
regionSource: deployRegion?.annotation ?? null,
|
|
421
|
+
envVars: envVarNames(envVars)
|
|
422
|
+
},
|
|
202
423
|
durationMs: deployDurationMs,
|
|
203
424
|
localPin: localPinResult
|
|
204
425
|
},
|
|
205
|
-
warnings: [
|
|
206
|
-
|
|
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}`]
|
|
207
432
|
};
|
|
208
433
|
}
|
|
209
|
-
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) {
|
|
210
451
|
ensurePreviewAppMode(context);
|
|
211
|
-
const
|
|
212
|
-
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);
|
|
213
458
|
if (!selectedApp) return {
|
|
214
459
|
command: "app.list-deploys",
|
|
215
460
|
result: {
|
|
216
461
|
projectId,
|
|
462
|
+
verboseContext: toAppVerboseContext(target),
|
|
217
463
|
app: null,
|
|
218
464
|
deployments: []
|
|
219
465
|
},
|
|
@@ -233,6 +479,7 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
233
479
|
command: "app.list-deploys",
|
|
234
480
|
result: {
|
|
235
481
|
projectId,
|
|
482
|
+
verboseContext: toAppVerboseContext(target),
|
|
236
483
|
app: {
|
|
237
484
|
id: deploymentsResult.app.id,
|
|
238
485
|
name: deploymentsResult.app.name
|
|
@@ -243,14 +490,19 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
243
490
|
nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
|
|
244
491
|
};
|
|
245
492
|
}
|
|
246
|
-
async function runAppShow(context, appName, projectRef) {
|
|
493
|
+
async function runAppShow(context, appName, projectRef, configTarget) {
|
|
247
494
|
ensurePreviewAppMode(context);
|
|
248
|
-
const
|
|
249
|
-
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);
|
|
250
501
|
if (!selectedApp) return {
|
|
251
502
|
command: "app.show",
|
|
252
503
|
result: {
|
|
253
504
|
projectId,
|
|
505
|
+
verboseContext: toAppVerboseContext(target),
|
|
254
506
|
app: null,
|
|
255
507
|
liveDeployment: null,
|
|
256
508
|
liveUrl: null,
|
|
@@ -273,6 +525,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
273
525
|
command: "app.show",
|
|
274
526
|
result: {
|
|
275
527
|
projectId,
|
|
528
|
+
verboseContext: toAppVerboseContext(target),
|
|
276
529
|
app: {
|
|
277
530
|
id: deploymentsResult.app.id,
|
|
278
531
|
name: deploymentsResult.app.name
|
|
@@ -319,9 +572,14 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
319
572
|
nextSteps: []
|
|
320
573
|
};
|
|
321
574
|
}
|
|
322
|
-
async function runAppOpen(context, appName, projectRef) {
|
|
575
|
+
async function runAppOpen(context, appName, projectRef, configTarget) {
|
|
323
576
|
ensurePreviewAppMode(context);
|
|
324
|
-
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
|
+
});
|
|
325
583
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
326
584
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
327
585
|
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
@@ -346,6 +604,7 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
346
604
|
command: "app.open",
|
|
347
605
|
result: {
|
|
348
606
|
projectId,
|
|
607
|
+
verboseContext: toAppVerboseContext(target),
|
|
349
608
|
app: {
|
|
350
609
|
id: deploymentsResult.app.id,
|
|
351
610
|
name: deploymentsResult.app.name
|
|
@@ -484,15 +743,19 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
484
743
|
});
|
|
485
744
|
}
|
|
486
745
|
}
|
|
487
|
-
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
746
|
+
async function runAppLogs(context, appName, deploymentId, projectRef, configTarget) {
|
|
488
747
|
ensurePreviewAppMode(context);
|
|
489
|
-
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
|
+
});
|
|
490
754
|
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
491
755
|
if (!context.flags.json && !context.flags.quiet) {
|
|
492
756
|
const lines = renderCommandHeader(context.ui, {
|
|
493
757
|
commandLabel: "app logs",
|
|
494
758
|
description: "Streaming logs for the selected deployment.",
|
|
495
|
-
docsPath: "docs/product/command-spec.md#prisma-cli-app-logs---app-name---deployment-id",
|
|
496
759
|
rows: [
|
|
497
760
|
{
|
|
498
761
|
key: "project",
|
|
@@ -609,9 +872,14 @@ function writeLogRecord(context, record) {
|
|
|
609
872
|
if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
|
|
610
873
|
}
|
|
611
874
|
}
|
|
612
|
-
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
875
|
+
async function runAppPromote(context, deploymentId, appName, projectRef, configTarget) {
|
|
613
876
|
ensurePreviewAppMode(context);
|
|
614
|
-
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
|
+
});
|
|
615
883
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
616
884
|
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
617
885
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -627,7 +895,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
627
895
|
appId: selectedApp.id,
|
|
628
896
|
deploymentId: targetDeployment.id,
|
|
629
897
|
signal: context.runtime.signal,
|
|
630
|
-
progress:
|
|
898
|
+
progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
631
899
|
}).catch((error) => {
|
|
632
900
|
throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
|
|
633
901
|
});
|
|
@@ -636,6 +904,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
636
904
|
command: "app.promote",
|
|
637
905
|
result: {
|
|
638
906
|
projectId,
|
|
907
|
+
verboseContext: toAppVerboseContext(target),
|
|
639
908
|
app: {
|
|
640
909
|
id: deploymentsResult.app.id,
|
|
641
910
|
name: deploymentsResult.app.name
|
|
@@ -650,9 +919,14 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
650
919
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
651
920
|
};
|
|
652
921
|
}
|
|
653
|
-
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
922
|
+
async function runAppRollback(context, appName, deploymentId, projectRef, configTarget) {
|
|
654
923
|
ensurePreviewAppMode(context);
|
|
655
|
-
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
|
+
});
|
|
656
930
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
657
931
|
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
658
932
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -669,7 +943,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
669
943
|
appId: selectedApp.id,
|
|
670
944
|
deploymentId: targetDeployment.id,
|
|
671
945
|
signal: context.runtime.signal,
|
|
672
|
-
progress:
|
|
946
|
+
progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
673
947
|
}).catch((error) => {
|
|
674
948
|
throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
|
|
675
949
|
});
|
|
@@ -678,6 +952,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
678
952
|
command: "app.rollback",
|
|
679
953
|
result: {
|
|
680
954
|
projectId,
|
|
955
|
+
verboseContext: toAppVerboseContext(target),
|
|
681
956
|
app: {
|
|
682
957
|
id: deploymentsResult.app.id,
|
|
683
958
|
name: deploymentsResult.app.name
|
|
@@ -693,9 +968,22 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
693
968
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
694
969
|
};
|
|
695
970
|
}
|
|
696
|
-
|
|
971
|
+
/**
|
|
972
|
+
* Removes an app and every deployment it owns in the resolved branch.
|
|
973
|
+
*
|
|
974
|
+
* @param branchName Scopes the removal to this branch. When omitted the branch
|
|
975
|
+
* is inferred from the local Git branch, falling back to the production branch.
|
|
976
|
+
*/
|
|
977
|
+
async function runAppRemove(context, appName, projectRef, configTarget, branchName) {
|
|
697
978
|
ensurePreviewAppMode(context);
|
|
698
|
-
const
|
|
979
|
+
const compute = await resolveComputeManagementContext(context, configTarget, "remove");
|
|
980
|
+
appName = appName ?? compute.configAppName;
|
|
981
|
+
if (branchName !== void 0 && branchName.trim() === "") throw usageError("The --branch value cannot be empty", "app remove scopes the removal to the given branch; an empty --branch would silently fall back to the inferred (possibly production) branch.", "Pass a non-empty branch name, e.g. --branch <branch>, or omit --branch to use the inferred branch.", ["prisma-cli app remove --app <name> --branch <branch>"], "app");
|
|
982
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
983
|
+
branch: branchName !== void 0 ? await resolveDeployBranch(context, branchName) : void 0,
|
|
984
|
+
commandName: "app remove",
|
|
985
|
+
projectDir: compute.projectDir
|
|
986
|
+
});
|
|
699
987
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
700
988
|
await confirmAppRemoval(context, selectedApp);
|
|
701
989
|
const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
@@ -706,6 +994,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
706
994
|
command: "app.remove",
|
|
707
995
|
result: {
|
|
708
996
|
projectId,
|
|
997
|
+
verboseContext: toAppVerboseContext(target),
|
|
709
998
|
app: {
|
|
710
999
|
id: removedApp.id,
|
|
711
1000
|
name: removedApp.name
|
|
@@ -718,6 +1007,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
718
1007
|
}
|
|
719
1008
|
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
720
1009
|
ensurePreviewAppMode(context);
|
|
1010
|
+
const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(/^app /, ""));
|
|
721
1011
|
const branch = resolveDomainBranch(options?.branchName);
|
|
722
1012
|
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
723
1013
|
code: "BRANCH_NOT_DEPLOYABLE",
|
|
@@ -733,10 +1023,11 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
733
1023
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
734
1024
|
branch,
|
|
735
1025
|
commandName,
|
|
736
|
-
envProjectId
|
|
1026
|
+
envProjectId,
|
|
1027
|
+
projectDir: compute.projectDir
|
|
737
1028
|
});
|
|
738
1029
|
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
739
|
-
explicitAppName: options?.appName,
|
|
1030
|
+
explicitAppName: options?.appName ?? compute.configAppName,
|
|
740
1031
|
explicitAppId: envAppId
|
|
741
1032
|
});
|
|
742
1033
|
await context.stateStore.setSelectedApp(projectId, {
|
|
@@ -749,7 +1040,7 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
749
1040
|
resultTarget: {
|
|
750
1041
|
workspace: target.workspace,
|
|
751
1042
|
project: target.project,
|
|
752
|
-
branch: target.branch,
|
|
1043
|
+
branch: toResultBranch(target.branch),
|
|
753
1044
|
app: {
|
|
754
1045
|
id: selectedApp.id,
|
|
755
1046
|
name: selectedApp.name
|
|
@@ -809,7 +1100,7 @@ function toAppDomainSummary(domain) {
|
|
|
809
1100
|
type: domain.type,
|
|
810
1101
|
url: domain.url,
|
|
811
1102
|
hostname: domain.hostname,
|
|
812
|
-
|
|
1103
|
+
appId: domain.appId,
|
|
813
1104
|
status: domain.status,
|
|
814
1105
|
foundryStatus: domain.foundryStatus,
|
|
815
1106
|
failureReason: domain.failureReason,
|
|
@@ -847,12 +1138,13 @@ async function confirmDomainRemoval(context, target, hostname) {
|
|
|
847
1138
|
if (!await confirmPrompt({
|
|
848
1139
|
input: context.runtime.stdin,
|
|
849
1140
|
output: context.output.stderr,
|
|
1141
|
+
signal: context.runtime.signal,
|
|
850
1142
|
message: `Detach ${hostname} from App "${target.app.name}"?`,
|
|
851
1143
|
initialValue: false
|
|
852
1144
|
})) 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");
|
|
853
1145
|
}
|
|
854
1146
|
function domainCommandError(command, error, hostname) {
|
|
855
|
-
if (error instanceof
|
|
1147
|
+
if (error instanceof DomainApiError) {
|
|
856
1148
|
if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
|
|
857
1149
|
if (command === "add" && error.status === 400) return new CliError({
|
|
858
1150
|
code: "DOMAIN_HOSTNAME_INVALID",
|
|
@@ -1021,27 +1313,19 @@ async function sleep(milliseconds, signal) {
|
|
|
1021
1313
|
});
|
|
1022
1314
|
}
|
|
1023
1315
|
async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
};
|
|
1034
|
-
return {
|
|
1035
|
-
appName: options.explicitAppName,
|
|
1036
|
-
region: PREVIEW_DEFAULT_REGION,
|
|
1037
|
-
displayName: options.explicitAppName,
|
|
1038
|
-
annotation: "set by --app",
|
|
1039
|
-
firstDeploy: options.firstDeploy
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1316
|
+
const newAppRegion = deployNewAppRegion(options.configRegion);
|
|
1317
|
+
if (options.explicitAppName) return resolveDeployAppByName(context, apps, {
|
|
1318
|
+
name: options.explicitAppName,
|
|
1319
|
+
matchedAnnotation: "set by --app",
|
|
1320
|
+
newAnnotation: "set by --app",
|
|
1321
|
+
requestedRegion: options.configRegion,
|
|
1322
|
+
newAppRegion,
|
|
1323
|
+
firstDeploy: options.firstDeploy
|
|
1324
|
+
});
|
|
1042
1325
|
if (options.explicitAppId) {
|
|
1043
1326
|
const matched = apps.find((app) => app.id === options.explicitAppId);
|
|
1044
1327
|
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");
|
|
1328
|
+
assertDeployRegionMatchesExistingApp(matched, options.configRegion);
|
|
1045
1329
|
return {
|
|
1046
1330
|
appId: matched.id,
|
|
1047
1331
|
displayName: matched.name,
|
|
@@ -1049,25 +1333,54 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
|
1049
1333
|
firstDeploy: options.firstDeploy
|
|
1050
1334
|
};
|
|
1051
1335
|
}
|
|
1336
|
+
if (options.configAppName) {
|
|
1337
|
+
const configName = options.configAppName;
|
|
1338
|
+
return resolveDeployAppByName(context, apps, {
|
|
1339
|
+
name: configName.value,
|
|
1340
|
+
matchedAnnotation: configName.annotation,
|
|
1341
|
+
newAnnotation: configName.annotation,
|
|
1342
|
+
requestedRegion: options.configRegion,
|
|
1343
|
+
newAppRegion,
|
|
1344
|
+
firstDeploy: options.firstDeploy
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1052
1347
|
const inferredName = await options.inferName();
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1348
|
+
const newAnnotation = inferredName.source === "package-name" ? "created from package.json" : "created from directory name";
|
|
1349
|
+
return resolveDeployAppByName(context, apps, {
|
|
1350
|
+
name: inferredName.name,
|
|
1351
|
+
matchedAnnotation: "existing app on this branch",
|
|
1352
|
+
newAnnotation,
|
|
1353
|
+
requestedRegion: options.configRegion,
|
|
1354
|
+
newAppRegion,
|
|
1060
1355
|
firstDeploy: options.firstDeploy
|
|
1061
|
-
};
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
async function resolveDeployAppByName(context, apps, options) {
|
|
1359
|
+
const matches = findAppsByName(apps, options.name);
|
|
1360
|
+
if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.name, options.requestedRegion, options.newAppRegion, options.firstDeploy);
|
|
1361
|
+
const matched = matches[0];
|
|
1362
|
+
if (matched) {
|
|
1363
|
+
assertDeployRegionMatchesExistingApp(matched, options.requestedRegion);
|
|
1364
|
+
return {
|
|
1365
|
+
appId: matched.id,
|
|
1366
|
+
displayName: matched.name,
|
|
1367
|
+
annotation: options.matchedAnnotation,
|
|
1368
|
+
firstDeploy: options.firstDeploy
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1062
1371
|
return {
|
|
1063
|
-
appName:
|
|
1064
|
-
region:
|
|
1065
|
-
displayName:
|
|
1066
|
-
annotation:
|
|
1372
|
+
appName: options.name,
|
|
1373
|
+
region: options.newAppRegion,
|
|
1374
|
+
displayName: options.name,
|
|
1375
|
+
annotation: options.newAnnotation,
|
|
1067
1376
|
firstDeploy: options.firstDeploy
|
|
1068
1377
|
};
|
|
1069
1378
|
}
|
|
1070
|
-
|
|
1379
|
+
function assertDeployRegionMatchesExistingApp(app, requestedRegion) {
|
|
1380
|
+
if (requestedRegion?.annotation !== "set by --region" || !app.region || app.region === requestedRegion.value) return;
|
|
1381
|
+
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");
|
|
1382
|
+
}
|
|
1383
|
+
async function resolveAmbiguousDeployApp(context, matches, targetName, requestedRegion, newAppRegion, firstDeploy) {
|
|
1071
1384
|
if (canPrompt(context)) {
|
|
1072
1385
|
const createNew = "__create_new_app__";
|
|
1073
1386
|
const cancel = "__cancel__";
|
|
@@ -1093,11 +1406,12 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
|
|
|
1093
1406
|
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");
|
|
1094
1407
|
if (selected === createNew) return {
|
|
1095
1408
|
appName: targetName,
|
|
1096
|
-
region:
|
|
1409
|
+
region: newAppRegion,
|
|
1097
1410
|
displayName: targetName,
|
|
1098
1411
|
annotation: "created from package.json",
|
|
1099
1412
|
firstDeploy
|
|
1100
1413
|
};
|
|
1414
|
+
assertDeployRegionMatchesExistingApp(selected, requestedRegion);
|
|
1101
1415
|
return {
|
|
1102
1416
|
appId: selected.id,
|
|
1103
1417
|
displayName: selected.name,
|
|
@@ -1119,6 +1433,9 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
|
|
|
1119
1433
|
nextSteps: ["prisma-cli app deploy --app <name>"]
|
|
1120
1434
|
});
|
|
1121
1435
|
}
|
|
1436
|
+
function deployNewAppRegion(configRegion) {
|
|
1437
|
+
return configRegion?.value;
|
|
1438
|
+
}
|
|
1122
1439
|
async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
|
|
1123
1440
|
if (explicitAppName) {
|
|
1124
1441
|
const matched = findAppByName(apps, explicitAppName);
|
|
@@ -1161,6 +1478,7 @@ async function confirmAppRemoval(context, app) {
|
|
|
1161
1478
|
await textPrompt({
|
|
1162
1479
|
input: context.runtime.stdin,
|
|
1163
1480
|
output: context.output.stderr,
|
|
1481
|
+
signal: context.runtime.signal,
|
|
1164
1482
|
message: `Type ${app.name} to confirm app removal`,
|
|
1165
1483
|
placeholder: app.name,
|
|
1166
1484
|
validate: (value) => value === app.name ? void 0 : `Type "${app.name}" to confirm removal.`
|
|
@@ -1193,13 +1511,21 @@ function requireDeploymentForApp(deployments, deploymentId, appName) {
|
|
|
1193
1511
|
nextSteps: ["prisma-cli app list-deploys"]
|
|
1194
1512
|
});
|
|
1195
1513
|
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Resolves the app's live deployment from the app pointer, the provider's live
|
|
1516
|
+
* flag, then locally cached state.
|
|
1517
|
+
*
|
|
1518
|
+
* @returns the live deployment id, or null when no authoritative signal exists.
|
|
1519
|
+
* Callers must treat null as "not known to be live" rather than assuming the
|
|
1520
|
+
* newest deployment is live.
|
|
1521
|
+
*/
|
|
1196
1522
|
async function resolveCurrentLiveDeploymentId(context, projectId, app, deployments) {
|
|
1197
1523
|
if (app.liveDeploymentId && deployments.some((deployment) => deployment.id === app.liveDeploymentId)) return app.liveDeploymentId;
|
|
1198
1524
|
const providerLiveDeployment = deployments.find((deployment) => deployment.live === true);
|
|
1199
1525
|
if (providerLiveDeployment) return providerLiveDeployment.id;
|
|
1200
1526
|
const knownLiveDeploymentId = await context.stateStore.readKnownLiveDeployment(projectId, app.id);
|
|
1201
1527
|
if (knownLiveDeploymentId && deployments.some((deployment) => deployment.id === knownLiveDeploymentId)) return knownLiveDeploymentId;
|
|
1202
|
-
return
|
|
1528
|
+
return null;
|
|
1203
1529
|
}
|
|
1204
1530
|
function buildAppShowNextSteps(liveUrl, liveDeployment, deployments) {
|
|
1205
1531
|
const nextSteps = [];
|
|
@@ -1258,7 +1584,7 @@ async function requirePreviewAppProviderWithClient(context) {
|
|
|
1258
1584
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
1259
1585
|
return {
|
|
1260
1586
|
client,
|
|
1261
|
-
provider:
|
|
1587
|
+
provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
|
|
1262
1588
|
};
|
|
1263
1589
|
}
|
|
1264
1590
|
function createPreviewLogAuthOptions(env, signal) {
|
|
@@ -1300,20 +1626,29 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1300
1626
|
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1301
1627
|
const authState = await requireAuthenticatedAuthState(context);
|
|
1302
1628
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
1303
|
-
const
|
|
1629
|
+
const resolvedResult = await resolveProjectTarget({
|
|
1304
1630
|
context,
|
|
1305
1631
|
workspace: authState.workspace,
|
|
1306
1632
|
explicitProject,
|
|
1307
1633
|
envProjectId: options?.envProjectId,
|
|
1634
|
+
projectDir: options?.projectDir,
|
|
1308
1635
|
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
1309
1636
|
commandName: options?.commandName
|
|
1310
1637
|
});
|
|
1311
|
-
|
|
1638
|
+
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
1639
|
+
const resolved = resolvedResult.value;
|
|
1640
|
+
const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1641
|
+
const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
|
|
1642
|
+
projectId: resolved.project.id,
|
|
1643
|
+
branchName: requested.name,
|
|
1644
|
+
signal: context.runtime.signal
|
|
1645
|
+
});
|
|
1312
1646
|
return {
|
|
1313
1647
|
...resolved,
|
|
1314
|
-
branch: {
|
|
1315
|
-
|
|
1316
|
-
|
|
1648
|
+
branch: remoteBranch ?? {
|
|
1649
|
+
id: null,
|
|
1650
|
+
name: requested.name,
|
|
1651
|
+
kind: toBranchKind(requested.name)
|
|
1317
1652
|
}
|
|
1318
1653
|
};
|
|
1319
1654
|
}
|
|
@@ -1322,7 +1657,7 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1322
1657
|
if (!workspace) throw workspaceRequiredError();
|
|
1323
1658
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1324
1659
|
const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
|
|
1325
|
-
if (explicitProject) return
|
|
1660
|
+
if (explicitProject) return withRemoteDeployBranch(provider, {
|
|
1326
1661
|
workspace,
|
|
1327
1662
|
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1328
1663
|
resolution: {
|
|
@@ -1331,25 +1666,25 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1331
1666
|
targetNameSource: "explicit"
|
|
1332
1667
|
},
|
|
1333
1668
|
localPinAction: "linked"
|
|
1334
|
-
}, branch);
|
|
1669
|
+
}, branch, context.runtime.signal);
|
|
1335
1670
|
if (options.createProjectName) {
|
|
1336
1671
|
const projectName = options.createProjectName.trim();
|
|
1337
1672
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1338
|
-
return
|
|
1673
|
+
return withRemoteDeployBranch(provider, {
|
|
1339
1674
|
workspace,
|
|
1340
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1675
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, options.createProjectRegion)),
|
|
1341
1676
|
resolution: {
|
|
1342
1677
|
projectSource: "created",
|
|
1343
1678
|
targetName: projectName,
|
|
1344
1679
|
targetNameSource: "explicit"
|
|
1345
1680
|
},
|
|
1346
1681
|
localPinAction: "created"
|
|
1347
|
-
}, branch);
|
|
1682
|
+
}, branch, context.runtime.signal);
|
|
1348
1683
|
}
|
|
1349
1684
|
if (options.envProjectId) {
|
|
1350
1685
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1351
1686
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1352
|
-
return
|
|
1687
|
+
return withRemoteDeployBranch(provider, {
|
|
1353
1688
|
workspace,
|
|
1354
1689
|
project: toProjectSummary(project),
|
|
1355
1690
|
resolution: {
|
|
@@ -1357,14 +1692,18 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1357
1692
|
targetName: options.envProjectId,
|
|
1358
1693
|
targetNameSource: "env"
|
|
1359
1694
|
}
|
|
1360
|
-
}, branch);
|
|
1695
|
+
}, branch, context.runtime.signal);
|
|
1361
1696
|
}
|
|
1362
1697
|
const localPin = options.localPin;
|
|
1363
1698
|
if (localPin.kind === "present") {
|
|
1364
|
-
if (localPin.pin.workspaceId !== workspace.id) throw
|
|
1699
|
+
if (localPin.pin.workspaceId !== workspace.id) throw localProjectWorkspaceMismatchError({
|
|
1700
|
+
pinnedWorkspaceId: localPin.pin.workspaceId,
|
|
1701
|
+
pinnedProjectId: localPin.pin.projectId,
|
|
1702
|
+
activeWorkspace: workspace
|
|
1703
|
+
});
|
|
1365
1704
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
1366
1705
|
if (!project) throw localResolutionPinStaleError();
|
|
1367
|
-
return
|
|
1706
|
+
return withRemoteDeployBranch(provider, {
|
|
1368
1707
|
workspace,
|
|
1369
1708
|
project: toProjectSummary(project),
|
|
1370
1709
|
resolution: {
|
|
@@ -1372,10 +1711,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1372
1711
|
targetName: project.name,
|
|
1373
1712
|
targetNameSource: "local-pin"
|
|
1374
1713
|
}
|
|
1375
|
-
}, branch);
|
|
1714
|
+
}, branch, context.runtime.signal);
|
|
1376
1715
|
}
|
|
1377
1716
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
1378
|
-
if (platformMapping && platformMapping.workspace.id === workspace.id) return
|
|
1717
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
|
|
1379
1718
|
workspace,
|
|
1380
1719
|
project: toProjectSummary(platformMapping),
|
|
1381
1720
|
resolution: {
|
|
@@ -1383,15 +1722,15 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1383
1722
|
targetName: platformMapping.name,
|
|
1384
1723
|
targetNameSource: "platform-mapping"
|
|
1385
1724
|
}
|
|
1386
|
-
}, branch);
|
|
1387
|
-
if (canPrompt(context) && !context.flags.yes) return
|
|
1725
|
+
}, branch, context.runtime.signal);
|
|
1726
|
+
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, options.createProjectRegion), branch, context.runtime.signal);
|
|
1388
1727
|
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1389
1728
|
}
|
|
1390
|
-
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1729
|
+
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, createProjectRegion) {
|
|
1391
1730
|
const setup = await promptForProjectSetupChoice({
|
|
1392
1731
|
context,
|
|
1393
1732
|
projects,
|
|
1394
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
|
|
1733
|
+
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, createProjectRegion),
|
|
1395
1734
|
cancel: {
|
|
1396
1735
|
why: "Deploy needs a Project before it can continue.",
|
|
1397
1736
|
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
@@ -1409,9 +1748,10 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
|
|
|
1409
1748
|
localPinAction: setup.action
|
|
1410
1749
|
};
|
|
1411
1750
|
}
|
|
1412
|
-
async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
|
|
1751
|
+
async function createProjectForDeploySetup(provider, projectName, workspace, signal, region) {
|
|
1413
1752
|
const created = await provider.createProject({
|
|
1414
1753
|
name: projectName,
|
|
1754
|
+
region,
|
|
1415
1755
|
signal
|
|
1416
1756
|
}).catch((error) => {
|
|
1417
1757
|
throw projectCreateFailedError(error, projectName, workspace, {
|
|
@@ -1427,21 +1767,50 @@ async function createProjectForDeploySetup(provider, projectName, workspace, sig
|
|
|
1427
1767
|
return {
|
|
1428
1768
|
id: created.id,
|
|
1429
1769
|
name: created.name,
|
|
1770
|
+
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {},
|
|
1430
1771
|
workspace
|
|
1431
1772
|
};
|
|
1432
1773
|
}
|
|
1433
|
-
function
|
|
1774
|
+
async function withRemoteDeployBranch(provider, target, branch, signal) {
|
|
1775
|
+
const remoteBranch = await provider.resolveBranch(target.project.id, {
|
|
1776
|
+
branchName: branch.name,
|
|
1777
|
+
signal
|
|
1778
|
+
});
|
|
1434
1779
|
return {
|
|
1435
1780
|
...target,
|
|
1436
1781
|
branch: {
|
|
1437
|
-
|
|
1438
|
-
|
|
1782
|
+
id: remoteBranch.id,
|
|
1783
|
+
name: remoteBranch.name,
|
|
1784
|
+
kind: remoteBranch.role
|
|
1439
1785
|
}
|
|
1440
1786
|
};
|
|
1441
1787
|
}
|
|
1442
1788
|
function toBranchKind(name) {
|
|
1443
1789
|
return name === "production" || name === "main" ? "production" : "preview";
|
|
1444
1790
|
}
|
|
1791
|
+
function toResultBranch(branch) {
|
|
1792
|
+
return {
|
|
1793
|
+
id: branch.id,
|
|
1794
|
+
name: branch.name,
|
|
1795
|
+
kind: branch.kind
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
function toAppVerboseContext(target) {
|
|
1799
|
+
return {
|
|
1800
|
+
workspace: target.workspace,
|
|
1801
|
+
project: target.project,
|
|
1802
|
+
branch: target.branch,
|
|
1803
|
+
resolution: target.resolution
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
function toBranchDatabaseDeployBranch(branch) {
|
|
1807
|
+
if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
|
|
1808
|
+
return {
|
|
1809
|
+
id: branch.id,
|
|
1810
|
+
name: branch.name,
|
|
1811
|
+
kind: branch.kind
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1445
1814
|
function assertExclusiveDeployProjectInputs(options) {
|
|
1446
1815
|
const provided = [
|
|
1447
1816
|
options.projectRef ? "--project" : null,
|
|
@@ -1470,58 +1839,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1470
1839
|
annotation: "default"
|
|
1471
1840
|
};
|
|
1472
1841
|
}
|
|
1473
|
-
async function
|
|
1474
|
-
|
|
1475
|
-
if (
|
|
1476
|
-
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
})).trim();
|
|
1481
|
-
if (head.startsWith("ref: refs/heads/")) return head.slice(16);
|
|
1482
|
-
} catch (error) {
|
|
1483
|
-
if (signal.aborted) throw error;
|
|
1484
|
-
return null;
|
|
1842
|
+
async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
|
|
1843
|
+
let config;
|
|
1844
|
+
if (options?.preloaded !== void 0) config = options.preloaded;
|
|
1845
|
+
else {
|
|
1846
|
+
const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
|
|
1847
|
+
if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
|
|
1848
|
+
config = loaded.value;
|
|
1485
1849
|
}
|
|
1486
|
-
|
|
1850
|
+
if (!config) {
|
|
1851
|
+
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");
|
|
1852
|
+
return {
|
|
1853
|
+
config: null,
|
|
1854
|
+
target: null
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
|
|
1858
|
+
const selected = selectComputeDeployTarget(config, requestedTarget);
|
|
1859
|
+
if (selected.isErr()) {
|
|
1860
|
+
if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
|
|
1861
|
+
config,
|
|
1862
|
+
target: null
|
|
1863
|
+
};
|
|
1864
|
+
throw computeConfigErrorToCliError(selected.error, commandName);
|
|
1865
|
+
}
|
|
1866
|
+
return {
|
|
1867
|
+
config,
|
|
1868
|
+
target: selected.value
|
|
1869
|
+
};
|
|
1487
1870
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1871
|
+
/**
|
|
1872
|
+
* Compute-config context for app management commands: the project directory
|
|
1873
|
+
* (where `.prisma/local.json` lives) and the config-selected app name, which
|
|
1874
|
+
* ranks below `--app` but above the remembered app selection.
|
|
1875
|
+
*/
|
|
1876
|
+
async function resolveComputeManagementContext(context, configTarget, commandName) {
|
|
1877
|
+
const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
|
|
1878
|
+
return {
|
|
1879
|
+
projectDir: compute.config?.configDir ?? context.runtime.cwd,
|
|
1880
|
+
configAppName: compute.target?.name ?? compute.target?.key ?? void 0
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
async function resolveComputeAppDir(context, compute) {
|
|
1884
|
+
if (!compute.config || !compute.target) return context.runtime.cwd;
|
|
1885
|
+
const appDir = computeTargetAppDir(compute.config, compute.target);
|
|
1886
|
+
if (!compute.target.root) return appDir;
|
|
1887
|
+
context.runtime.signal.throwIfAborted();
|
|
1490
1888
|
try {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
signal
|
|
1494
|
-
});
|
|
1495
|
-
if (raw.startsWith("gitdir:")) return path.join(path.resolve(path.dirname(gitPath), raw.slice(7).trim()), "HEAD");
|
|
1889
|
+
await access(appDir);
|
|
1890
|
+
context.runtime.signal.throwIfAborted();
|
|
1496
1891
|
} catch (error) {
|
|
1497
|
-
if (signal.aborted) throw error;
|
|
1892
|
+
if (context.runtime.signal.aborted) throw error;
|
|
1893
|
+
throw new CliError({
|
|
1894
|
+
code: "COMPUTE_CONFIG_INVALID",
|
|
1895
|
+
domain: "app",
|
|
1896
|
+
summary: `App root "${compute.target.root}" does not exist`,
|
|
1897
|
+
why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
|
|
1898
|
+
fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
|
|
1899
|
+
where: appDir,
|
|
1900
|
+
meta: {
|
|
1901
|
+
appRoot: compute.target.root,
|
|
1902
|
+
appDir
|
|
1903
|
+
},
|
|
1904
|
+
exitCode: 2,
|
|
1905
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1906
|
+
});
|
|
1498
1907
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1908
|
+
return appDir;
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* `prisma.app.json` is no longer read or written. A leftover file that
|
|
1912
|
+
* matches the effective settings only warns; one with custom values fails
|
|
1913
|
+
* with migration guidance so builds never silently change.
|
|
1914
|
+
*/
|
|
1915
|
+
async function handleLegacyBuildSettings(context, appDir, effective) {
|
|
1916
|
+
const legacy = await detectLegacyBuildSettings({
|
|
1917
|
+
appPath: appDir,
|
|
1918
|
+
effective,
|
|
1919
|
+
signal: context.runtime.signal
|
|
1920
|
+
});
|
|
1921
|
+
switch (legacy.kind) {
|
|
1922
|
+
case "absent": return [];
|
|
1923
|
+
case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
|
|
1924
|
+
case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
|
|
1925
|
+
case "custom": {
|
|
1926
|
+
const buildBlock = [
|
|
1927
|
+
"build: {",
|
|
1928
|
+
` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
|
|
1929
|
+
` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
|
|
1930
|
+
"}"
|
|
1931
|
+
].join(" ");
|
|
1932
|
+
throw new CliError({
|
|
1933
|
+
code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
|
|
1934
|
+
domain: "app",
|
|
1935
|
+
summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
|
|
1936
|
+
why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
|
|
1937
|
+
fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
|
|
1938
|
+
where: legacy.configPath,
|
|
1939
|
+
meta: {
|
|
1940
|
+
configPath: legacy.configPath,
|
|
1941
|
+
buildCommand: legacy.buildCommand,
|
|
1942
|
+
outputDirectory: legacy.outputDirectory
|
|
1943
|
+
},
|
|
1944
|
+
exitCode: 2,
|
|
1945
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1507
1948
|
}
|
|
1508
1949
|
}
|
|
1509
1950
|
async function resolveDeployFramework(context, options) {
|
|
1510
|
-
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1951
|
+
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
|
|
1511
1952
|
if (options.entrypoint) return {
|
|
1512
1953
|
key: "bun",
|
|
1513
1954
|
buildType: "bun",
|
|
1514
1955
|
displayName: "Bun",
|
|
1515
|
-
annotation: "set by --entry"
|
|
1956
|
+
annotation: options.entrypointAnnotation ?? "set by --entry"
|
|
1516
1957
|
};
|
|
1517
|
-
const detected = await detectDeployFramework(
|
|
1958
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
1518
1959
|
if (detected) return detected;
|
|
1519
|
-
throw frameworkNotDetectedError(
|
|
1960
|
+
throw frameworkNotDetectedError(options.appDir);
|
|
1520
1961
|
}
|
|
1521
|
-
function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
1962
|
+
function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
|
|
1522
1963
|
if (requestedHttpPort) return {
|
|
1523
1964
|
port: parseDeployHttpPort(requestedHttpPort),
|
|
1524
|
-
annotation: "set by --http-port"
|
|
1965
|
+
annotation: requestedHttpPortAnnotation ?? "set by --http-port"
|
|
1525
1966
|
};
|
|
1526
1967
|
return {
|
|
1527
1968
|
port: FRAMEWORK_DEFAULT_HTTP_PORT,
|
|
@@ -1536,8 +1977,8 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
|
|
|
1536
1977
|
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1537
1978
|
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
1538
1979
|
if (packageEntrypoint) return packageEntrypoint;
|
|
1539
|
-
|
|
1540
|
-
|
|
1980
|
+
const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
|
|
1981
|
+
if (!defaultEntrypoint) return;
|
|
1541
1982
|
signal.throwIfAborted();
|
|
1542
1983
|
try {
|
|
1543
1984
|
await access(path.join(cwd, defaultEntrypoint));
|
|
@@ -1550,109 +1991,52 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
|
|
|
1550
1991
|
}
|
|
1551
1992
|
}
|
|
1552
1993
|
async function detectDeployFramework(cwd, signal) {
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
key:
|
|
1563
|
-
buildType:
|
|
1564
|
-
displayName:
|
|
1565
|
-
annotation
|
|
1566
|
-
};
|
|
1567
|
-
if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
|
|
1568
|
-
key: "tanstack-start",
|
|
1569
|
-
buildType: "tanstack-start",
|
|
1570
|
-
displayName: "TanStack Start",
|
|
1571
|
-
annotation: "detected from package.json"
|
|
1994
|
+
const detected = await detectComputeAppFromDirectory({
|
|
1995
|
+
appPath: cwd,
|
|
1996
|
+
signal
|
|
1997
|
+
});
|
|
1998
|
+
if (!detected) return null;
|
|
1999
|
+
let annotation = "detected from package.json";
|
|
2000
|
+
if (detected.configFile?.standaloneOutput) annotation = "standalone output detected";
|
|
2001
|
+
else if (detected.configFile) annotation = `detected from ${path.basename(detected.configFile.path)}`;
|
|
2002
|
+
return {
|
|
2003
|
+
key: detected.framework,
|
|
2004
|
+
buildType: detected.buildType,
|
|
2005
|
+
displayName: detected.frameworkName,
|
|
2006
|
+
annotation
|
|
1572
2007
|
};
|
|
1573
|
-
return null;
|
|
1574
2008
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
"next.config.mjs",
|
|
1579
|
-
"next.config.cjs",
|
|
1580
|
-
"next.config.ts",
|
|
1581
|
-
"next.config.mts"
|
|
1582
|
-
]) {
|
|
1583
|
-
const filePath = path.join(cwd, candidate);
|
|
1584
|
-
signal.throwIfAborted();
|
|
1585
|
-
try {
|
|
1586
|
-
const content = await readFile(filePath, {
|
|
1587
|
-
encoding: "utf8",
|
|
1588
|
-
signal
|
|
1589
|
-
});
|
|
1590
|
-
return {
|
|
1591
|
-
exists: true,
|
|
1592
|
-
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
1593
|
-
};
|
|
1594
|
-
} catch (error) {
|
|
1595
|
-
if (signal.aborted) throw error;
|
|
1596
|
-
if (error.code !== "ENOENT") throw error;
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
2009
|
+
function frameworkFromUserFacingValue(value, annotation) {
|
|
2010
|
+
const framework = frameworkFromAlias(value);
|
|
2011
|
+
if (!framework) throw frameworkNotDetectedError(void 0, value);
|
|
1599
2012
|
return {
|
|
1600
|
-
|
|
1601
|
-
|
|
2013
|
+
key: framework.key,
|
|
2014
|
+
buildType: framework.buildType,
|
|
2015
|
+
displayName: framework.displayName,
|
|
2016
|
+
annotation
|
|
1602
2017
|
};
|
|
1603
2018
|
}
|
|
1604
|
-
function
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
case "next":
|
|
1616
|
-
case "next.js":
|
|
1617
|
-
case "nextjs": return {
|
|
1618
|
-
key: "nextjs",
|
|
1619
|
-
buildType: "nextjs",
|
|
1620
|
-
displayName: "Next.js",
|
|
1621
|
-
annotation
|
|
1622
|
-
};
|
|
1623
|
-
case "hono": return {
|
|
1624
|
-
key: "hono",
|
|
1625
|
-
buildType: "bun",
|
|
1626
|
-
displayName: "Hono",
|
|
1627
|
-
annotation
|
|
1628
|
-
};
|
|
1629
|
-
case "bun": return {
|
|
1630
|
-
key: "bun",
|
|
1631
|
-
buildType: "bun",
|
|
1632
|
-
displayName: "Bun",
|
|
1633
|
-
annotation
|
|
1634
|
-
};
|
|
1635
|
-
case "tanstack":
|
|
1636
|
-
case "tanstack-start":
|
|
1637
|
-
case "@tanstack/react-start":
|
|
1638
|
-
case "@tanstack/solid-start": return {
|
|
1639
|
-
key: "tanstack-start",
|
|
1640
|
-
buildType: "tanstack-start",
|
|
1641
|
-
displayName: "TanStack Start",
|
|
1642
|
-
annotation
|
|
1643
|
-
};
|
|
1644
|
-
default: throw frameworkNotDetectedError(void 0, value);
|
|
1645
|
-
}
|
|
2019
|
+
function assertConfigBackedBuildSettings(buildType) {
|
|
2020
|
+
if (isConfigBackedBuildType(buildType)) return;
|
|
2021
|
+
const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
|
|
2022
|
+
throw new CliError({
|
|
2023
|
+
code: "BUILD_SETTINGS_UNSUPPORTED",
|
|
2024
|
+
domain: "app",
|
|
2025
|
+
summary: `build settings are not supported for ${displayName} apps`,
|
|
2026
|
+
why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
|
|
2027
|
+
fix: "Remove the `build` block from prisma.compute.ts for this app.",
|
|
2028
|
+
exitCode: 2
|
|
2029
|
+
});
|
|
1646
2030
|
}
|
|
1647
2031
|
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1648
|
-
const supported =
|
|
2032
|
+
const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
|
|
1649
2033
|
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1650
2034
|
return new CliError({
|
|
1651
2035
|
code: "FRAMEWORK_NOT_DETECTED",
|
|
1652
2036
|
domain: "app",
|
|
1653
2037
|
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1654
2038
|
why: `Supported Beta frameworks: ${supported}.`,
|
|
1655
|
-
fix:
|
|
2039
|
+
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.`,
|
|
1656
2040
|
exitCode: 2,
|
|
1657
2041
|
nextSteps: [
|
|
1658
2042
|
"prisma-cli app deploy --framework nextjs",
|
|
@@ -1665,10 +2049,32 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
|
1665
2049
|
}
|
|
1666
2050
|
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1667
2051
|
if (context.flags.json || context.flags.quiet) return;
|
|
1668
|
-
const directory =
|
|
2052
|
+
const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
|
|
1669
2053
|
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1670
2054
|
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1671
2055
|
}
|
|
2056
|
+
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
2057
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
2058
|
+
const settings = resolution.settings;
|
|
2059
|
+
const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
|
|
2060
|
+
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [
|
|
2061
|
+
{
|
|
2062
|
+
label: "Build Command",
|
|
2063
|
+
value: settings.buildCommand ?? "none",
|
|
2064
|
+
origin: settings.buildCommandSource ?? void 0
|
|
2065
|
+
},
|
|
2066
|
+
{
|
|
2067
|
+
label: "Output Directory",
|
|
2068
|
+
value: settings.outputDirectory,
|
|
2069
|
+
origin: settings.outputDirectorySource ?? void 0
|
|
2070
|
+
},
|
|
2071
|
+
...settings.entrypoint ? [{
|
|
2072
|
+
label: "Entrypoint",
|
|
2073
|
+
value: settings.entrypoint,
|
|
2074
|
+
origin: settings.entrypointSource ?? void 0
|
|
2075
|
+
}] : []
|
|
2076
|
+
]).join("\n")}\n\n`);
|
|
2077
|
+
}
|
|
1672
2078
|
function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
|
|
1673
2079
|
if (context.flags.json || context.flags.quiet) return;
|
|
1674
2080
|
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
@@ -1685,6 +2091,7 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1685
2091
|
if (!await confirmPrompt({
|
|
1686
2092
|
input: context.runtime.stdin,
|
|
1687
2093
|
output: context.runtime.stderr,
|
|
2094
|
+
signal: context.runtime.signal,
|
|
1688
2095
|
message: "Customize build settings?",
|
|
1689
2096
|
initialValue: false
|
|
1690
2097
|
})) return {
|
|
@@ -1694,15 +2101,17 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1694
2101
|
const framework = frameworkFromUserFacingValue(await selectPrompt({
|
|
1695
2102
|
input: context.runtime.stdin,
|
|
1696
2103
|
output: context.runtime.stderr,
|
|
2104
|
+
signal: context.runtime.signal,
|
|
1697
2105
|
message: `Framework (${options.framework.displayName})`,
|
|
1698
|
-
choices:
|
|
1699
|
-
label:
|
|
1700
|
-
value: framework
|
|
2106
|
+
choices: FRAMEWORKS.map((framework) => ({
|
|
2107
|
+
label: framework.displayName,
|
|
2108
|
+
value: framework.key
|
|
1701
2109
|
}))
|
|
1702
2110
|
}), "set by you");
|
|
1703
2111
|
const requestedPort = await textPrompt({
|
|
1704
2112
|
input: context.runtime.stdin,
|
|
1705
2113
|
output: context.runtime.stderr,
|
|
2114
|
+
signal: context.runtime.signal,
|
|
1706
2115
|
message: `HTTP port (${options.runtime.port})`,
|
|
1707
2116
|
placeholder: String(options.runtime.port),
|
|
1708
2117
|
validate: validateDeployHttpPortText
|
|
@@ -1740,14 +2149,6 @@ function maybeRenderDeploySettingsPreview(context, options) {
|
|
|
1740
2149
|
value: `HTTP ${options.runtime.port}`
|
|
1741
2150
|
}]).join("\n")}\n\n`);
|
|
1742
2151
|
}
|
|
1743
|
-
function frameworkDisplayName(framework) {
|
|
1744
|
-
switch (framework) {
|
|
1745
|
-
case "nextjs": return "Next.js";
|
|
1746
|
-
case "hono": return "Hono";
|
|
1747
|
-
case "tanstack-start": return "TanStack Start";
|
|
1748
|
-
case "bun": return "Bun";
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
2152
|
function validateDeployHttpPortText(value) {
|
|
1752
2153
|
if (!value?.trim()) return;
|
|
1753
2154
|
try {
|
|
@@ -1761,6 +2162,11 @@ function formatDeployDirectory(cwd) {
|
|
|
1761
2162
|
const basename = path.basename(cwd);
|
|
1762
2163
|
return basename ? `./${basename}` : ".";
|
|
1763
2164
|
}
|
|
2165
|
+
function formatAppDirectoryLabel(cwd, appDir) {
|
|
2166
|
+
if (appDir === cwd) return formatDeployDirectory(cwd);
|
|
2167
|
+
const relative = path.relative(cwd, appDir).split(path.sep).join("/");
|
|
2168
|
+
return relative.startsWith("..") ? relative : `./${relative}`;
|
|
2169
|
+
}
|
|
1764
2170
|
async function readCurrentWorkspaceId(context) {
|
|
1765
2171
|
const state = await context.stateStore.read();
|
|
1766
2172
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
@@ -1769,26 +2175,37 @@ async function readCurrentWorkspaceId(context) {
|
|
|
1769
2175
|
function normalizeBuildType(requestedBuildType) {
|
|
1770
2176
|
if (!requestedBuildType) return "auto";
|
|
1771
2177
|
if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
|
|
1772
|
-
throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${
|
|
2178
|
+
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");
|
|
1773
2179
|
}
|
|
1774
2180
|
function isPreviewBuildType(value) {
|
|
1775
|
-
return
|
|
2181
|
+
return APP_BUILD_TYPES.includes(value);
|
|
1776
2182
|
}
|
|
1777
2183
|
function getBuildTypeExamples(commandName) {
|
|
1778
|
-
return
|
|
2184
|
+
return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
|
|
1779
2185
|
return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
|
|
1780
2186
|
});
|
|
1781
2187
|
}
|
|
1782
2188
|
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
1783
|
-
if (buildType !== "auto" && buildType
|
|
2189
|
+
if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
|
|
1784
2190
|
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");
|
|
1785
2191
|
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");
|
|
1786
2192
|
}
|
|
1787
2193
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
2194
|
+
/**
|
|
2195
|
+
* Resolves the framework for `app run` with the same detection as deploy, so
|
|
2196
|
+
* a repo that deploys without flags also runs without flags. Local dev server
|
|
2197
|
+
* support is intentionally narrower than deploy build support: only Next.js
|
|
2198
|
+
* and Bun/Hono have dev servers in the current preview.
|
|
2199
|
+
*/
|
|
2200
|
+
async function resolveLocalRunFramework(context, options) {
|
|
2201
|
+
if (options.requestedBuildType === "auto" && options.entrypoint) return frameworkFromUserFacingValue("bun", "set by --entry");
|
|
2202
|
+
if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
|
|
2203
|
+
if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME$1}`);
|
|
2204
|
+
return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
|
|
2205
|
+
}
|
|
2206
|
+
const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
|
|
2207
|
+
if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
|
|
2208
|
+
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");
|
|
1792
2209
|
}
|
|
1793
2210
|
function parseLocalPort(requestedPort) {
|
|
1794
2211
|
if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
|
|
@@ -1805,6 +2222,19 @@ function parseDeployHttpPort(requestedPort) {
|
|
|
1805
2222
|
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");
|
|
1806
2223
|
return port;
|
|
1807
2224
|
}
|
|
2225
|
+
function parseDeployRegion(requestedRegion, source) {
|
|
2226
|
+
const region = requestedRegion.trim();
|
|
2227
|
+
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");
|
|
2228
|
+
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");
|
|
2229
|
+
return region;
|
|
2230
|
+
}
|
|
2231
|
+
function normalizeDeployRegionInput(region) {
|
|
2232
|
+
if (!region) return;
|
|
2233
|
+
return {
|
|
2234
|
+
...region,
|
|
2235
|
+
value: parseDeployRegion(region.value, region.annotation)
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
1808
2238
|
function ensurePreviewAppMode(context) {
|
|
1809
2239
|
if (isRealMode(context)) return;
|
|
1810
2240
|
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");
|
|
@@ -1861,7 +2291,7 @@ function appDeployFailedError(error, progress) {
|
|
|
1861
2291
|
}
|
|
1862
2292
|
if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
|
|
1863
2293
|
const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
|
|
1864
|
-
const recoveryLines = progress.
|
|
2294
|
+
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."];
|
|
1865
2295
|
const urlLines = progress.deploymentUrl ? [
|
|
1866
2296
|
"",
|
|
1867
2297
|
"URL",
|
|
@@ -1887,11 +2317,11 @@ function appDeployFailedError(error, progress) {
|
|
|
1887
2317
|
domain: "app",
|
|
1888
2318
|
summary: phaseHeadline,
|
|
1889
2319
|
why,
|
|
1890
|
-
fix: progress.
|
|
2320
|
+
fix: progress.deploymentId ? `Inspect logs with prisma-cli app logs --deployment ${progress.deploymentId}.` : "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
1891
2321
|
debug,
|
|
1892
2322
|
meta: {
|
|
1893
2323
|
phase: progress.containerLive ? "runtime_ready" : "deploy",
|
|
1894
|
-
deploymentId: progress.
|
|
2324
|
+
deploymentId: progress.deploymentId,
|
|
1895
2325
|
deploymentUrl: progress.deploymentUrl
|
|
1896
2326
|
},
|
|
1897
2327
|
humanLines,
|
|
@@ -1915,6 +2345,18 @@ function localResolutionPinStaleError() {
|
|
|
1915
2345
|
]
|
|
1916
2346
|
});
|
|
1917
2347
|
}
|
|
2348
|
+
function localPinReadErrorToDeployError(error) {
|
|
2349
|
+
return matchError(error, {
|
|
2350
|
+
LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
|
|
2351
|
+
LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
|
|
2352
|
+
LocalResolutionPinReadAbortedError: (error) => {
|
|
2353
|
+
throw error;
|
|
2354
|
+
},
|
|
2355
|
+
UnhandledException: (error) => {
|
|
2356
|
+
throw error;
|
|
2357
|
+
}
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
1918
2360
|
function readDeployEnvOverride(context, name) {
|
|
1919
2361
|
const value = context.runtime.env[name]?.trim();
|
|
1920
2362
|
return value ? value : void 0;
|
|
@@ -1993,14 +2435,12 @@ function isAutoBuildDetectionError(error) {
|
|
|
1993
2435
|
return error instanceof Error && error.message.startsWith("Entrypoint is required.");
|
|
1994
2436
|
}
|
|
1995
2437
|
function formatBuildTypeName(buildType) {
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
case "tanstack-start": return "TanStack Start";
|
|
2001
|
-
case "bun": return "Bun";
|
|
2002
|
-
case "auto": return "Auto";
|
|
2438
|
+
if (buildType === "auto") return "Auto";
|
|
2439
|
+
for (let index = FRAMEWORKS.length - 1; index >= 0; index -= 1) {
|
|
2440
|
+
const framework = FRAMEWORKS[index];
|
|
2441
|
+
if (framework?.buildType === buildType) return framework.displayName;
|
|
2003
2442
|
}
|
|
2443
|
+
return buildType;
|
|
2004
2444
|
}
|
|
2005
2445
|
function removeFailedError(summary, error, nextSteps) {
|
|
2006
2446
|
return new CliError({
|
|
@@ -2037,4 +2477,4 @@ function toOptionalEnvVars(envVars) {
|
|
|
2037
2477
|
return Object.keys(envVars).length > 0 ? envVars : void 0;
|
|
2038
2478
|
}
|
|
2039
2479
|
//#endregion
|
|
2040
|
-
export { runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };
|
|
2480
|
+
export { detectDeployFramework, runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };
|