@prisma/cli 3.0.0-beta.1 → 3.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +12 -4
- package/dist/adapters/mock-api.js +81 -2
- package/dist/adapters/token-storage.js +63 -22
- package/dist/cli.js +17 -2
- package/dist/cli2.js +7 -3
- package/dist/commands/app/index.js +22 -10
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/database/index.js +159 -0
- package/dist/commands/env.js +8 -4
- package/dist/controllers/app-env-api.js +54 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +283 -129
- package/dist/controllers/app.js +247 -106
- package/dist/controllers/auth.js +8 -8
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +153 -82
- package/dist/lib/app/branch-database-deploy.js +373 -0
- package/dist/lib/app/branch-database.js +316 -0
- package/dist/lib/app/bun-project.js +12 -5
- package/dist/lib/app/deploy-output.js +10 -1
- 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 +34 -18
- package/dist/lib/app/preview-branch-database.js +102 -0
- package/dist/lib/app/preview-build-settings.js +385 -0
- package/dist/lib/app/preview-build.js +272 -81
- package/dist/lib/app/preview-provider.js +163 -54
- package/dist/lib/app/production-deploy-gate.js +161 -0
- package/dist/lib/auth/auth-ops.js +30 -21
- package/dist/lib/auth/guard.js +3 -2
- package/dist/lib/auth/login.js +109 -14
- package/dist/lib/database/provider.js +167 -0
- package/dist/lib/diagnostics.js +15 -0
- package/dist/lib/git/local-branch.js +41 -0
- package/dist/lib/git/local-status.js +57 -0
- package/dist/lib/project/interactive-setup.js +1 -1
- package/dist/lib/project/local-pin.js +182 -33
- package/dist/lib/project/resolution.js +203 -49
- package/dist/lib/project/setup.js +59 -15
- package/dist/presenters/app-env.js +149 -14
- package/dist/presenters/app.js +170 -20
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +63 -39
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/command-meta.js +103 -13
- package/dist/shell/command-runner.js +34 -6
- package/dist/shell/diagnostics-output.js +63 -0
- package/dist/shell/errors.js +12 -1
- package/dist/shell/output.js +10 -1
- package/dist/shell/runtime.js +3 -3
- package/dist/shell/ui.js +23 -1
- package/dist/shell/update-check.js +247 -0
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/dist/use-cases/project.js +2 -1
- package/package.json +12 -10
package/dist/controllers/app.js
CHANGED
|
@@ -7,19 +7,23 @@ import { canPrompt } from "../shell/runtime.js";
|
|
|
7
7
|
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
8
8
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
9
|
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
|
-
import {
|
|
11
|
-
import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
|
|
10
|
+
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
11
|
+
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
12
12
|
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
13
|
import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
|
|
14
14
|
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
15
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";
|
|
16
|
+
import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
17
|
+
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
18
18
|
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
19
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
20
|
+
import { resolveOrCreatePreviewBuildSettings } from "../lib/app/preview-build-settings.js";
|
|
19
21
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
20
22
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
23
|
+
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
21
24
|
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
22
25
|
import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
26
|
+
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
23
27
|
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
24
28
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
25
29
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
@@ -27,6 +31,7 @@ import { listRealWorkspaceProjects } from "./project.js";
|
|
|
27
31
|
import { access, readFile } from "node:fs/promises";
|
|
28
32
|
import path from "node:path";
|
|
29
33
|
import open from "open";
|
|
34
|
+
import { Result, matchError } from "better-result";
|
|
30
35
|
//#region src/controllers/app.ts
|
|
31
36
|
const DEPLOY_FRAMEWORKS = [
|
|
32
37
|
"nextjs",
|
|
@@ -48,7 +53,8 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
|
|
|
48
53
|
const { artifact, buildType: actualBuildType } = await executePreviewBuild({
|
|
49
54
|
appPath: context.runtime.cwd,
|
|
50
55
|
entrypoint,
|
|
51
|
-
buildType
|
|
56
|
+
buildType,
|
|
57
|
+
signal: context.runtime.signal
|
|
52
58
|
});
|
|
53
59
|
return {
|
|
54
60
|
command: "app.build",
|
|
@@ -78,12 +84,13 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
|
|
|
78
84
|
buildType: resolvedBuildType,
|
|
79
85
|
entrypoint,
|
|
80
86
|
port,
|
|
81
|
-
env: context.runtime.env
|
|
87
|
+
env: context.runtime.env,
|
|
88
|
+
signal: context.runtime.signal
|
|
82
89
|
});
|
|
83
90
|
} catch (error) {
|
|
84
91
|
throw runFailedError("Local app run failed", error);
|
|
85
92
|
}
|
|
86
|
-
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM")
|
|
93
|
+
if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") throw new DOMException("Command canceled", "AbortError");
|
|
87
94
|
else if (runResult.exitCode !== 0) throw runFailedError("Local app run failed", `The ${formatFrameworkName(runResult.framework)} process exited with code ${runResult.exitCode}.`, runResult.exitCode);
|
|
88
95
|
return {
|
|
89
96
|
command: "app.run",
|
|
@@ -106,9 +113,9 @@ async function runAppDeploy(context, appName, options) {
|
|
|
106
113
|
createProjectName: options?.createProjectName,
|
|
107
114
|
envProjectId
|
|
108
115
|
});
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
|
|
117
|
+
if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
|
|
118
|
+
const localPin = localPinReadResult.value;
|
|
112
119
|
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
113
120
|
if (options?.httpPort) parseDeployHttpPort(options.httpPort);
|
|
114
121
|
assertSupportedEntrypointForRequestedDeployShape({
|
|
@@ -124,8 +131,10 @@ async function runAppDeploy(context, appName, options) {
|
|
|
124
131
|
let localPinResult;
|
|
125
132
|
if (target.localPinAction) {
|
|
126
133
|
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
135
|
+
const projectSetup = setupResult.value;
|
|
136
|
+
localPinResult = projectSetup.localPin;
|
|
137
|
+
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
129
138
|
}
|
|
130
139
|
let framework = await resolveDeployFramework(context, {
|
|
131
140
|
requestedFramework: options?.framework,
|
|
@@ -133,12 +142,12 @@ async function runAppDeploy(context, appName, options) {
|
|
|
133
142
|
});
|
|
134
143
|
let runtime = resolveDeployRuntime(options?.httpPort, framework);
|
|
135
144
|
assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
|
|
136
|
-
const envVars = toOptionalEnvVars(
|
|
145
|
+
const envVars = toOptionalEnvVars(await parseEnvInputs(context.runtime.cwd, options?.envAssignments, { commandName: "deploy" }));
|
|
137
146
|
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
138
147
|
explicitAppName: appName,
|
|
139
148
|
explicitAppId: envAppId,
|
|
140
149
|
firstDeploy: Boolean(target.localPinAction),
|
|
141
|
-
inferName: () => inferTargetName(context.runtime.cwd)
|
|
150
|
+
inferName: () => inferTargetName(context.runtime.cwd, context.runtime.signal)
|
|
142
151
|
});
|
|
143
152
|
await maybeRenderDeploySetupBlock(context, {
|
|
144
153
|
includeDirectory: !target.localPinAction,
|
|
@@ -156,10 +165,27 @@ async function runAppDeploy(context, appName, options) {
|
|
|
156
165
|
});
|
|
157
166
|
framework = customized.framework;
|
|
158
167
|
runtime = customized.runtime;
|
|
168
|
+
const productionDeployGate = await enforceProductionDeployGate(context, provider, {
|
|
169
|
+
appId: selectedApp.appId,
|
|
170
|
+
appName: selectedApp.displayName,
|
|
171
|
+
branchKind: target.branch.kind,
|
|
172
|
+
prod: options?.prod === true
|
|
173
|
+
});
|
|
159
174
|
const buildType = framework.buildType;
|
|
160
175
|
assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
|
|
161
|
-
const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint);
|
|
176
|
+
const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint, context.runtime.signal);
|
|
177
|
+
const buildSettingsResolution = await resolveOrCreatePreviewBuildSettings({
|
|
178
|
+
appPath: context.runtime.cwd,
|
|
179
|
+
buildType,
|
|
180
|
+
signal: context.runtime.signal
|
|
181
|
+
});
|
|
182
|
+
maybeRenderDeployBuildSettings(context, buildSettingsResolution);
|
|
162
183
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
184
|
+
const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
|
|
185
|
+
db: options?.db,
|
|
186
|
+
providedEnvVars: envVars,
|
|
187
|
+
firstProductionDeploy: productionDeployGate.firstProductionDeploy
|
|
188
|
+
});
|
|
163
189
|
const progressState = createPreviewDeployProgressState();
|
|
164
190
|
const deployStartedAt = Date.now();
|
|
165
191
|
const deployResult = await provider.deployApp({
|
|
@@ -171,9 +197,11 @@ async function runAppDeploy(context, appName, options) {
|
|
|
171
197
|
region: selectedApp.region,
|
|
172
198
|
entrypoint,
|
|
173
199
|
buildType,
|
|
200
|
+
buildSettings: buildSettingsResolution.settings,
|
|
174
201
|
portMapping,
|
|
175
202
|
envVars,
|
|
176
203
|
interaction: void 0,
|
|
204
|
+
signal: context.runtime.signal,
|
|
177
205
|
progress: createPreviewDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
|
|
178
206
|
}).catch((error) => {
|
|
179
207
|
throw appDeployFailedError(error, progressState);
|
|
@@ -189,17 +217,42 @@ async function runAppDeploy(context, appName, options) {
|
|
|
189
217
|
result: {
|
|
190
218
|
workspace: target.workspace,
|
|
191
219
|
project: target.project,
|
|
192
|
-
branch: target.branch,
|
|
220
|
+
branch: toResultBranch(target.branch),
|
|
193
221
|
resolution: target.resolution,
|
|
222
|
+
branchDatabase: branchDatabaseSetup.result,
|
|
194
223
|
app: {
|
|
195
224
|
id: deployResult.app.id,
|
|
196
225
|
name: deployResult.app.name
|
|
197
226
|
},
|
|
198
227
|
deployment: deployResult.deployment,
|
|
228
|
+
deploySettings: {
|
|
229
|
+
config: {
|
|
230
|
+
path: buildSettingsResolution.relativeConfigPath,
|
|
231
|
+
status: buildSettingsResolution.status
|
|
232
|
+
},
|
|
233
|
+
buildCommand: {
|
|
234
|
+
value: buildSettingsResolution.settings.buildCommand,
|
|
235
|
+
source: buildSettingsResolution.settings.buildCommandSource
|
|
236
|
+
},
|
|
237
|
+
outputDirectory: {
|
|
238
|
+
value: buildSettingsResolution.settings.outputDirectory,
|
|
239
|
+
source: buildSettingsResolution.settings.outputDirectorySource
|
|
240
|
+
},
|
|
241
|
+
framework: {
|
|
242
|
+
key: framework.key,
|
|
243
|
+
buildType,
|
|
244
|
+
name: framework.displayName,
|
|
245
|
+
source: framework.annotation
|
|
246
|
+
},
|
|
247
|
+
entrypoint: entrypoint ?? null,
|
|
248
|
+
httpPort: runtime.port,
|
|
249
|
+
region: selectedApp.region ?? null,
|
|
250
|
+
envVars: envVarNames(envVars)
|
|
251
|
+
},
|
|
199
252
|
durationMs: deployDurationMs,
|
|
200
253
|
localPin: localPinResult
|
|
201
254
|
},
|
|
202
|
-
warnings:
|
|
255
|
+
warnings: branchDatabaseSetup.warnings,
|
|
203
256
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
|
|
204
257
|
};
|
|
205
258
|
}
|
|
@@ -211,13 +264,14 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
211
264
|
command: "app.list-deploys",
|
|
212
265
|
result: {
|
|
213
266
|
projectId,
|
|
267
|
+
verboseContext: toAppVerboseContext(target),
|
|
214
268
|
app: null,
|
|
215
269
|
deployments: []
|
|
216
270
|
},
|
|
217
271
|
warnings: [],
|
|
218
272
|
nextSteps: ["prisma-cli app deploy"]
|
|
219
273
|
};
|
|
220
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
274
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
221
275
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app deploy"]);
|
|
222
276
|
});
|
|
223
277
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -230,6 +284,7 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
230
284
|
command: "app.list-deploys",
|
|
231
285
|
result: {
|
|
232
286
|
projectId,
|
|
287
|
+
verboseContext: toAppVerboseContext(target),
|
|
233
288
|
app: {
|
|
234
289
|
id: deploymentsResult.app.id,
|
|
235
290
|
name: deploymentsResult.app.name
|
|
@@ -248,6 +303,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
248
303
|
command: "app.show",
|
|
249
304
|
result: {
|
|
250
305
|
projectId,
|
|
306
|
+
verboseContext: toAppVerboseContext(target),
|
|
251
307
|
app: null,
|
|
252
308
|
liveDeployment: null,
|
|
253
309
|
liveUrl: null,
|
|
@@ -256,7 +312,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
256
312
|
warnings: [],
|
|
257
313
|
nextSteps: ["prisma-cli app deploy"]
|
|
258
314
|
};
|
|
259
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
315
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
260
316
|
throw deployFailedError("Failed to inspect app", error, ["prisma-cli app list-deploys"]);
|
|
261
317
|
});
|
|
262
318
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -270,6 +326,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
270
326
|
command: "app.show",
|
|
271
327
|
result: {
|
|
272
328
|
projectId,
|
|
329
|
+
verboseContext: toAppVerboseContext(target),
|
|
273
330
|
app: {
|
|
274
331
|
id: deploymentsResult.app.id,
|
|
275
332
|
name: deploymentsResult.app.name
|
|
@@ -284,7 +341,7 @@ async function runAppShow(context, appName, projectRef) {
|
|
|
284
341
|
}
|
|
285
342
|
async function runAppShowDeploy(context, deploymentId) {
|
|
286
343
|
ensurePreviewAppMode(context);
|
|
287
|
-
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId).catch((error) => {
|
|
344
|
+
const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
288
345
|
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
289
346
|
});
|
|
290
347
|
if (!deployment) throw new CliError({
|
|
@@ -321,7 +378,7 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
321
378
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
|
|
322
379
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
323
380
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
324
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
381
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
325
382
|
throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
|
|
326
383
|
});
|
|
327
384
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -334,11 +391,16 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
334
391
|
if (!liveDeployment) throw noDeploymentsError("No deployments available to open", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
|
|
335
392
|
if (!deploymentsResult.app.liveUrl) throw featureUnavailableError("Live URL is not available for the selected app", "Deployments exist, but the provider does not expose a stable live service URL for this app yet.", "Run prisma-cli app show to inspect the current deployment state and try again after the app reports a live URL.", ["prisma-cli app show"], "app");
|
|
336
393
|
const shouldOpen = canPrompt(context);
|
|
337
|
-
if (shouldOpen)
|
|
394
|
+
if (shouldOpen) {
|
|
395
|
+
context.runtime.signal.throwIfAborted();
|
|
396
|
+
await open(deploymentsResult.app.liveUrl);
|
|
397
|
+
context.runtime.signal.throwIfAborted();
|
|
398
|
+
}
|
|
338
399
|
return {
|
|
339
400
|
command: "app.open",
|
|
340
401
|
result: {
|
|
341
402
|
projectId,
|
|
403
|
+
verboseContext: toAppVerboseContext(target),
|
|
342
404
|
app: {
|
|
343
405
|
id: deploymentsResult.app.id,
|
|
344
406
|
name: deploymentsResult.app.name
|
|
@@ -355,7 +417,8 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
355
417
|
const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
|
|
356
418
|
const added = await target.provider.addDomain({
|
|
357
419
|
appId: target.app.id,
|
|
358
|
-
hostname: normalizedHostname
|
|
420
|
+
hostname: normalizedHostname,
|
|
421
|
+
signal: context.runtime.signal
|
|
359
422
|
}).catch((error) => {
|
|
360
423
|
throw domainCommandError("add", error, normalizedHostname);
|
|
361
424
|
});
|
|
@@ -373,8 +436,8 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
373
436
|
async function runAppDomainShow(context, hostname, options) {
|
|
374
437
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
375
438
|
const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
|
|
376
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
|
|
377
|
-
const detail = await target.provider.showDomain(domain.id).catch((error) => {
|
|
439
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show", context.runtime.signal);
|
|
440
|
+
const detail = await target.provider.showDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
378
441
|
throw domainCommandError("show", error, normalizedHostname);
|
|
379
442
|
});
|
|
380
443
|
return {
|
|
@@ -390,9 +453,9 @@ async function runAppDomainShow(context, hostname, options) {
|
|
|
390
453
|
async function runAppDomainRemove(context, hostname, options) {
|
|
391
454
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
392
455
|
const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
|
|
393
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
|
|
456
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove", context.runtime.signal);
|
|
394
457
|
await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
|
|
395
|
-
await target.provider.removeDomain(domain.id).catch((error) => {
|
|
458
|
+
await target.provider.removeDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
396
459
|
throw domainCommandError("remove", error, normalizedHostname);
|
|
397
460
|
});
|
|
398
461
|
return {
|
|
@@ -409,8 +472,8 @@ async function runAppDomainRemove(context, hostname, options) {
|
|
|
409
472
|
async function runAppDomainRetry(context, hostname, options) {
|
|
410
473
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
411
474
|
const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
|
|
412
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
|
|
413
|
-
const retried = await target.provider.retryDomain(domain.id).catch((error) => {
|
|
475
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry", context.runtime.signal);
|
|
476
|
+
const retried = await target.provider.retryDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
|
|
414
477
|
throw domainCommandError("retry", error, normalizedHostname);
|
|
415
478
|
});
|
|
416
479
|
return {
|
|
@@ -427,7 +490,7 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
427
490
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
428
491
|
const timeoutMs = parseDomainWaitTimeout(options?.timeout);
|
|
429
492
|
const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
|
|
430
|
-
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
|
|
493
|
+
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait", context.runtime.signal);
|
|
431
494
|
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
|
|
432
495
|
`app domain wait -> Waiting for ${normalizedHostname} to become active.`,
|
|
433
496
|
"",
|
|
@@ -470,8 +533,8 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
470
533
|
exitCode: 1,
|
|
471
534
|
nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
|
|
472
535
|
});
|
|
473
|
-
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)));
|
|
474
|
-
current = await target.provider.showDomain(current.id).catch((error) => {
|
|
536
|
+
await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)), context.runtime.signal);
|
|
537
|
+
current = await target.provider.showDomain(current.id, { signal: context.runtime.signal }).catch((error) => {
|
|
475
538
|
throw domainCommandError("wait", error, normalizedHostname);
|
|
476
539
|
});
|
|
477
540
|
}
|
|
@@ -504,6 +567,7 @@ async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
|
504
567
|
}
|
|
505
568
|
await provider.streamDeploymentLogs({
|
|
506
569
|
deploymentId: target.deployment.id,
|
|
570
|
+
signal: context.runtime.signal,
|
|
507
571
|
onRecord: (record) => writeLogRecord(context, record)
|
|
508
572
|
}).catch((error) => {
|
|
509
573
|
throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
|
|
@@ -513,7 +577,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
513
577
|
if (appName) {
|
|
514
578
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
515
579
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
516
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
580
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
517
581
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
518
582
|
});
|
|
519
583
|
const deployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
|
|
@@ -526,7 +590,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
526
590
|
deployment
|
|
527
591
|
};
|
|
528
592
|
}
|
|
529
|
-
const shown = await provider.showDeployment(deploymentId).catch((error) => {
|
|
593
|
+
const shown = await provider.showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
|
|
530
594
|
throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
|
|
531
595
|
});
|
|
532
596
|
if (!shown) throw new CliError({
|
|
@@ -569,7 +633,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
|
|
|
569
633
|
async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
|
|
570
634
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
|
|
571
635
|
if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
|
|
572
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
636
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
573
637
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
574
638
|
});
|
|
575
639
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -604,7 +668,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
604
668
|
ensurePreviewAppMode(context);
|
|
605
669
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
|
|
606
670
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
607
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
671
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
608
672
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
609
673
|
});
|
|
610
674
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -617,6 +681,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
617
681
|
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
618
682
|
appId: selectedApp.id,
|
|
619
683
|
deploymentId: targetDeployment.id,
|
|
684
|
+
signal: context.runtime.signal,
|
|
620
685
|
progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
621
686
|
}).catch((error) => {
|
|
622
687
|
throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
|
|
@@ -626,6 +691,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
626
691
|
command: "app.promote",
|
|
627
692
|
result: {
|
|
628
693
|
projectId,
|
|
694
|
+
verboseContext: toAppVerboseContext(target),
|
|
629
695
|
app: {
|
|
630
696
|
id: deploymentsResult.app.id,
|
|
631
697
|
name: deploymentsResult.app.name
|
|
@@ -644,7 +710,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
644
710
|
ensurePreviewAppMode(context);
|
|
645
711
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
|
|
646
712
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
647
|
-
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
713
|
+
const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
648
714
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
649
715
|
});
|
|
650
716
|
const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
|
|
@@ -658,6 +724,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
658
724
|
if (!targetAlreadyLive) await provider.promoteDeployment({
|
|
659
725
|
appId: selectedApp.id,
|
|
660
726
|
deploymentId: targetDeployment.id,
|
|
727
|
+
signal: context.runtime.signal,
|
|
661
728
|
progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
|
|
662
729
|
}).catch((error) => {
|
|
663
730
|
throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
|
|
@@ -667,6 +734,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
667
734
|
command: "app.rollback",
|
|
668
735
|
result: {
|
|
669
736
|
projectId,
|
|
737
|
+
verboseContext: toAppVerboseContext(target),
|
|
670
738
|
app: {
|
|
671
739
|
id: deploymentsResult.app.id,
|
|
672
740
|
name: deploymentsResult.app.name
|
|
@@ -687,7 +755,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
687
755
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
|
|
688
756
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
689
757
|
await confirmAppRemoval(context, selectedApp);
|
|
690
|
-
const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
|
|
758
|
+
const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
|
|
691
759
|
throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
|
|
692
760
|
});
|
|
693
761
|
const warnings = await cleanupRemovedAppState(context, projectId, removedApp.id);
|
|
@@ -695,6 +763,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
695
763
|
command: "app.remove",
|
|
696
764
|
result: {
|
|
697
765
|
projectId,
|
|
766
|
+
verboseContext: toAppVerboseContext(target),
|
|
698
767
|
app: {
|
|
699
768
|
id: removedApp.id,
|
|
700
769
|
name: removedApp.name
|
|
@@ -738,7 +807,7 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
|
|
|
738
807
|
resultTarget: {
|
|
739
808
|
workspace: target.workspace,
|
|
740
809
|
project: target.project,
|
|
741
|
-
branch: target.branch,
|
|
810
|
+
branch: toResultBranch(target.branch),
|
|
742
811
|
app: {
|
|
743
812
|
id: selectedApp.id,
|
|
744
813
|
name: selectedApp.name
|
|
@@ -762,8 +831,8 @@ async function resolveDomainAppSelection(context, projectId, apps, options) {
|
|
|
762
831
|
if (selectedApp) return selectedApp;
|
|
763
832
|
throw usageError("Custom domain requires an existing app on the production branch", "The resolved production branch does not have an app that can receive a custom domain.", "Deploy or promote an app to production first, then rerun the domain command.", ["prisma-cli app deploy --branch production", "prisma-cli app show"], "app");
|
|
764
833
|
}
|
|
765
|
-
async function resolveDomainByHostname(provider, appId, hostname, command) {
|
|
766
|
-
const matched = (await provider.listDomains(appId).catch((error) => {
|
|
834
|
+
async function resolveDomainByHostname(provider, appId, hostname, command, signal) {
|
|
835
|
+
const matched = (await provider.listDomains(appId, { signal }).catch((error) => {
|
|
767
836
|
throw domainCommandError(command, error, hostname);
|
|
768
837
|
})).find((domain) => sameDomainHostname(domain.hostname, hostname));
|
|
769
838
|
if (matched) return matched;
|
|
@@ -994,9 +1063,20 @@ function formatElapsed(milliseconds) {
|
|
|
994
1063
|
const remainingSeconds = seconds % 60;
|
|
995
1064
|
return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
|
|
996
1065
|
}
|
|
997
|
-
async function sleep(milliseconds) {
|
|
1066
|
+
async function sleep(milliseconds, signal) {
|
|
998
1067
|
if (milliseconds <= 0) return;
|
|
999
|
-
|
|
1068
|
+
signal.throwIfAborted();
|
|
1069
|
+
await new Promise((resolve, reject) => {
|
|
1070
|
+
const onAbort = () => {
|
|
1071
|
+
clearTimeout(timeout);
|
|
1072
|
+
reject(signal.reason);
|
|
1073
|
+
};
|
|
1074
|
+
const timeout = setTimeout(() => {
|
|
1075
|
+
signal.removeEventListener("abort", onAbort);
|
|
1076
|
+
resolve();
|
|
1077
|
+
}, milliseconds);
|
|
1078
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1079
|
+
});
|
|
1000
1080
|
}
|
|
1001
1081
|
async function resolveDeployAppSelection(context, projectId, apps, options) {
|
|
1002
1082
|
if (options.explicitAppName) {
|
|
@@ -1211,7 +1291,10 @@ function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
|
|
|
1211
1291
|
});
|
|
1212
1292
|
}
|
|
1213
1293
|
async function listApps(context, provider, projectId, branchName) {
|
|
1214
|
-
return provider.listApps(projectId, {
|
|
1294
|
+
return provider.listApps(projectId, {
|
|
1295
|
+
branchName,
|
|
1296
|
+
signal: context.runtime.signal
|
|
1297
|
+
}).then(sortApps).catch((error) => {
|
|
1215
1298
|
if (isMissingProjectError(error)) throw new CliError({
|
|
1216
1299
|
code: "PROJECT_NOT_FOUND",
|
|
1217
1300
|
domain: "project",
|
|
@@ -1229,20 +1312,20 @@ async function requirePreviewAppProvider(context) {
|
|
|
1229
1312
|
return provider;
|
|
1230
1313
|
}
|
|
1231
1314
|
async function requirePreviewAppProviderWithClient(context) {
|
|
1232
|
-
const client = await requireComputeAuth(context.runtime.env);
|
|
1315
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
1233
1316
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
1234
1317
|
return {
|
|
1235
1318
|
client,
|
|
1236
|
-
provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env))
|
|
1319
|
+
provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
|
|
1237
1320
|
};
|
|
1238
1321
|
}
|
|
1239
|
-
function createPreviewLogAuthOptions(env) {
|
|
1322
|
+
function createPreviewLogAuthOptions(env, signal) {
|
|
1240
1323
|
const rawToken = env[SERVICE_TOKEN_ENV_VAR]?.trim();
|
|
1241
1324
|
if (rawToken) return {
|
|
1242
1325
|
baseUrl: getApiBaseUrl(env),
|
|
1243
1326
|
getToken: async () => rawToken
|
|
1244
1327
|
};
|
|
1245
|
-
const tokenStorage = new FileTokenStorage(env);
|
|
1328
|
+
const tokenStorage = new FileTokenStorage(env, signal);
|
|
1246
1329
|
return {
|
|
1247
1330
|
baseUrl: getApiBaseUrl(env),
|
|
1248
1331
|
getToken: async () => {
|
|
@@ -1275,18 +1358,21 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1275
1358
|
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1276
1359
|
const authState = await requireAuthenticatedAuthState(context);
|
|
1277
1360
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
1278
|
-
const
|
|
1361
|
+
const resolvedResult = await resolveProjectTarget({
|
|
1279
1362
|
context,
|
|
1280
1363
|
workspace: authState.workspace,
|
|
1281
1364
|
explicitProject,
|
|
1282
1365
|
envProjectId: options?.envProjectId,
|
|
1283
|
-
listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
|
|
1366
|
+
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
1284
1367
|
commandName: options?.commandName
|
|
1285
1368
|
});
|
|
1369
|
+
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
1370
|
+
const resolved = resolvedResult.value;
|
|
1286
1371
|
const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1287
1372
|
return {
|
|
1288
1373
|
...resolved,
|
|
1289
1374
|
branch: {
|
|
1375
|
+
id: null,
|
|
1290
1376
|
name: branch.name,
|
|
1291
1377
|
kind: toBranchKind(branch.name)
|
|
1292
1378
|
}
|
|
@@ -1296,8 +1382,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1296
1382
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1297
1383
|
if (!workspace) throw workspaceRequiredError();
|
|
1298
1384
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1299
|
-
const projects = await listRealWorkspaceProjects(client, workspace);
|
|
1300
|
-
if (explicitProject) return
|
|
1385
|
+
const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
|
|
1386
|
+
if (explicitProject) return withRemoteDeployBranch(provider, {
|
|
1301
1387
|
workspace,
|
|
1302
1388
|
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1303
1389
|
resolution: {
|
|
@@ -1306,25 +1392,25 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1306
1392
|
targetNameSource: "explicit"
|
|
1307
1393
|
},
|
|
1308
1394
|
localPinAction: "linked"
|
|
1309
|
-
}, branch);
|
|
1395
|
+
}, branch, context.runtime.signal);
|
|
1310
1396
|
if (options.createProjectName) {
|
|
1311
1397
|
const projectName = options.createProjectName.trim();
|
|
1312
1398
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1313
|
-
return
|
|
1399
|
+
return withRemoteDeployBranch(provider, {
|
|
1314
1400
|
workspace,
|
|
1315
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
|
|
1401
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1316
1402
|
resolution: {
|
|
1317
1403
|
projectSource: "created",
|
|
1318
1404
|
targetName: projectName,
|
|
1319
1405
|
targetNameSource: "explicit"
|
|
1320
1406
|
},
|
|
1321
1407
|
localPinAction: "created"
|
|
1322
|
-
}, branch);
|
|
1408
|
+
}, branch, context.runtime.signal);
|
|
1323
1409
|
}
|
|
1324
1410
|
if (options.envProjectId) {
|
|
1325
1411
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1326
1412
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1327
|
-
return
|
|
1413
|
+
return withRemoteDeployBranch(provider, {
|
|
1328
1414
|
workspace,
|
|
1329
1415
|
project: toProjectSummary(project),
|
|
1330
1416
|
resolution: {
|
|
@@ -1332,14 +1418,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1332
1418
|
targetName: options.envProjectId,
|
|
1333
1419
|
targetNameSource: "env"
|
|
1334
1420
|
}
|
|
1335
|
-
}, branch);
|
|
1421
|
+
}, branch, context.runtime.signal);
|
|
1336
1422
|
}
|
|
1337
1423
|
const localPin = options.localPin;
|
|
1338
1424
|
if (localPin.kind === "present") {
|
|
1339
1425
|
if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
|
|
1340
1426
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
1341
1427
|
if (!project) throw localResolutionPinStaleError();
|
|
1342
|
-
return
|
|
1428
|
+
return withRemoteDeployBranch(provider, {
|
|
1343
1429
|
workspace,
|
|
1344
1430
|
project: toProjectSummary(project),
|
|
1345
1431
|
resolution: {
|
|
@@ -1347,10 +1433,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1347
1433
|
targetName: project.name,
|
|
1348
1434
|
targetNameSource: "local-pin"
|
|
1349
1435
|
}
|
|
1350
|
-
}, branch);
|
|
1436
|
+
}, branch, context.runtime.signal);
|
|
1351
1437
|
}
|
|
1352
1438
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
1353
|
-
if (platformMapping && platformMapping.workspace.id === workspace.id) return
|
|
1439
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
|
|
1354
1440
|
workspace,
|
|
1355
1441
|
project: toProjectSummary(platformMapping),
|
|
1356
1442
|
resolution: {
|
|
@@ -1358,15 +1444,15 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1358
1444
|
targetName: platformMapping.name,
|
|
1359
1445
|
targetNameSource: "platform-mapping"
|
|
1360
1446
|
}
|
|
1361
|
-
}, branch);
|
|
1362
|
-
if (canPrompt(context) && !context.flags.yes) return
|
|
1363
|
-
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd));
|
|
1447
|
+
}, branch, context.runtime.signal);
|
|
1448
|
+
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
|
|
1449
|
+
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1364
1450
|
}
|
|
1365
1451
|
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1366
1452
|
const setup = await promptForProjectSetupChoice({
|
|
1367
1453
|
context,
|
|
1368
1454
|
projects,
|
|
1369
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace),
|
|
1455
|
+
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
|
|
1370
1456
|
cancel: {
|
|
1371
1457
|
why: "Deploy needs a Project before it can continue.",
|
|
1372
1458
|
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
@@ -1384,8 +1470,11 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
|
|
|
1384
1470
|
localPinAction: setup.action
|
|
1385
1471
|
};
|
|
1386
1472
|
}
|
|
1387
|
-
async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
1388
|
-
const created = await provider.createProject({
|
|
1473
|
+
async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
|
|
1474
|
+
const created = await provider.createProject({
|
|
1475
|
+
name: projectName,
|
|
1476
|
+
signal
|
|
1477
|
+
}).catch((error) => {
|
|
1389
1478
|
throw projectCreateFailedError(error, projectName, workspace, {
|
|
1390
1479
|
nextSteps: [
|
|
1391
1480
|
"prisma-cli project list",
|
|
@@ -1402,18 +1491,46 @@ async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
|
1402
1491
|
workspace
|
|
1403
1492
|
};
|
|
1404
1493
|
}
|
|
1405
|
-
function
|
|
1494
|
+
async function withRemoteDeployBranch(provider, target, branch, signal) {
|
|
1495
|
+
const remoteBranch = await provider.resolveBranch(target.project.id, {
|
|
1496
|
+
branchName: branch.name,
|
|
1497
|
+
signal
|
|
1498
|
+
});
|
|
1406
1499
|
return {
|
|
1407
1500
|
...target,
|
|
1408
1501
|
branch: {
|
|
1409
|
-
|
|
1410
|
-
|
|
1502
|
+
id: remoteBranch.id,
|
|
1503
|
+
name: remoteBranch.name,
|
|
1504
|
+
kind: remoteBranch.role
|
|
1411
1505
|
}
|
|
1412
1506
|
};
|
|
1413
1507
|
}
|
|
1414
1508
|
function toBranchKind(name) {
|
|
1415
1509
|
return name === "production" || name === "main" ? "production" : "preview";
|
|
1416
1510
|
}
|
|
1511
|
+
function toResultBranch(branch) {
|
|
1512
|
+
return {
|
|
1513
|
+
id: branch.id,
|
|
1514
|
+
name: branch.name,
|
|
1515
|
+
kind: branch.kind
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function toAppVerboseContext(target) {
|
|
1519
|
+
return {
|
|
1520
|
+
workspace: target.workspace,
|
|
1521
|
+
project: target.project,
|
|
1522
|
+
branch: target.branch,
|
|
1523
|
+
resolution: target.resolution
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
function toBranchDatabaseDeployBranch(branch) {
|
|
1527
|
+
if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
|
|
1528
|
+
return {
|
|
1529
|
+
id: branch.id,
|
|
1530
|
+
name: branch.name,
|
|
1531
|
+
kind: branch.kind
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1417
1534
|
function assertExclusiveDeployProjectInputs(options) {
|
|
1418
1535
|
const provided = [
|
|
1419
1536
|
options.projectRef ? "--project" : null,
|
|
@@ -1432,7 +1549,7 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1432
1549
|
name: explicitBranchName,
|
|
1433
1550
|
annotation: "set by --branch"
|
|
1434
1551
|
};
|
|
1435
|
-
const gitBranch = await readLocalGitBranch(context.runtime.cwd);
|
|
1552
|
+
const gitBranch = await readLocalGitBranch(context.runtime.cwd, context.runtime.signal);
|
|
1436
1553
|
if (gitBranch) return {
|
|
1437
1554
|
name: gitBranch,
|
|
1438
1555
|
annotation: "from local Git branch"
|
|
@@ -1442,29 +1559,6 @@ async function resolveDeployBranch(context, explicitBranchName) {
|
|
|
1442
1559
|
annotation: "default"
|
|
1443
1560
|
};
|
|
1444
1561
|
}
|
|
1445
|
-
async function readLocalGitBranch(cwd) {
|
|
1446
|
-
const headPath = await resolveGitHeadPath(path.join(cwd, ".git"));
|
|
1447
|
-
if (!headPath) return null;
|
|
1448
|
-
try {
|
|
1449
|
-
const head = (await readFile(headPath, "utf8")).trim();
|
|
1450
|
-
if (head.startsWith("ref: refs/heads/")) return head.slice(16);
|
|
1451
|
-
} catch {
|
|
1452
|
-
return null;
|
|
1453
|
-
}
|
|
1454
|
-
return null;
|
|
1455
|
-
}
|
|
1456
|
-
async function resolveGitHeadPath(gitPath) {
|
|
1457
|
-
try {
|
|
1458
|
-
const raw = await readFile(gitPath, "utf8");
|
|
1459
|
-
if (raw.startsWith("gitdir:")) return path.join(path.resolve(path.dirname(gitPath), raw.slice(7).trim()), "HEAD");
|
|
1460
|
-
} catch {}
|
|
1461
|
-
try {
|
|
1462
|
-
await access(path.join(gitPath, "HEAD"));
|
|
1463
|
-
return path.join(gitPath, "HEAD");
|
|
1464
|
-
} catch {
|
|
1465
|
-
return null;
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
1562
|
async function resolveDeployFramework(context, options) {
|
|
1469
1563
|
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1470
1564
|
if (options.entrypoint) return {
|
|
@@ -1473,7 +1567,7 @@ async function resolveDeployFramework(context, options) {
|
|
|
1473
1567
|
displayName: "Bun",
|
|
1474
1568
|
annotation: "set by --entry"
|
|
1475
1569
|
};
|
|
1476
|
-
const detected = await detectDeployFramework(context.runtime.cwd);
|
|
1570
|
+
const detected = await detectDeployFramework(context.runtime.cwd, context.runtime.signal);
|
|
1477
1571
|
if (detected) return detected;
|
|
1478
1572
|
throw frameworkNotDetectedError(context.runtime.cwd);
|
|
1479
1573
|
}
|
|
@@ -1491,23 +1585,26 @@ function assertSupportedEntrypointForRequestedDeployShape(options) {
|
|
|
1491
1585
|
if (!options.requestedFramework) return;
|
|
1492
1586
|
assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
|
|
1493
1587
|
}
|
|
1494
|
-
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
|
|
1588
|
+
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signal) {
|
|
1495
1589
|
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1496
|
-
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
|
|
1590
|
+
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
1497
1591
|
if (packageEntrypoint) return packageEntrypoint;
|
|
1498
1592
|
if (framework.key !== "hono") return;
|
|
1499
1593
|
const defaultEntrypoint = "src/index.ts";
|
|
1594
|
+
signal.throwIfAborted();
|
|
1500
1595
|
try {
|
|
1501
1596
|
await access(path.join(cwd, defaultEntrypoint));
|
|
1597
|
+
signal.throwIfAborted();
|
|
1502
1598
|
return defaultEntrypoint;
|
|
1503
1599
|
} catch (error) {
|
|
1600
|
+
if (signal.aborted) throw error;
|
|
1504
1601
|
if (error.code !== "ENOENT") throw error;
|
|
1505
1602
|
return;
|
|
1506
1603
|
}
|
|
1507
1604
|
}
|
|
1508
|
-
async function detectDeployFramework(cwd) {
|
|
1509
|
-
const packageJson = await readBunPackageJson(cwd);
|
|
1510
|
-
const nextConfig = await detectNextConfig(cwd);
|
|
1605
|
+
async function detectDeployFramework(cwd, signal) {
|
|
1606
|
+
const packageJson = await readBunPackageJson(cwd, signal);
|
|
1607
|
+
const nextConfig = await detectNextConfig(cwd, signal);
|
|
1511
1608
|
if (nextConfig.exists || hasPackageDependency(packageJson, "next")) return {
|
|
1512
1609
|
key: "nextjs",
|
|
1513
1610
|
buildType: "nextjs",
|
|
@@ -1528,22 +1625,26 @@ async function detectDeployFramework(cwd) {
|
|
|
1528
1625
|
};
|
|
1529
1626
|
return null;
|
|
1530
1627
|
}
|
|
1531
|
-
async function detectNextConfig(cwd) {
|
|
1628
|
+
async function detectNextConfig(cwd, signal) {
|
|
1532
1629
|
for (const candidate of [
|
|
1533
1630
|
"next.config.js",
|
|
1534
1631
|
"next.config.mjs",
|
|
1535
|
-
"next.config.cjs",
|
|
1536
1632
|
"next.config.ts",
|
|
1537
1633
|
"next.config.mts"
|
|
1538
1634
|
]) {
|
|
1539
1635
|
const filePath = path.join(cwd, candidate);
|
|
1636
|
+
signal.throwIfAborted();
|
|
1540
1637
|
try {
|
|
1541
|
-
const content = await readFile(filePath,
|
|
1638
|
+
const content = await readFile(filePath, {
|
|
1639
|
+
encoding: "utf8",
|
|
1640
|
+
signal
|
|
1641
|
+
});
|
|
1542
1642
|
return {
|
|
1543
1643
|
exists: true,
|
|
1544
1644
|
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
|
|
1545
1645
|
};
|
|
1546
1646
|
} catch (error) {
|
|
1647
|
+
if (signal.aborted) throw error;
|
|
1547
1648
|
if (error.code !== "ENOENT") throw error;
|
|
1548
1649
|
}
|
|
1549
1650
|
}
|
|
@@ -1620,6 +1721,20 @@ async function maybeRenderDeploySetupBlock(context, details) {
|
|
|
1620
1721
|
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1621
1722
|
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1622
1723
|
}
|
|
1724
|
+
function maybeRenderDeployBuildSettings(context, resolution) {
|
|
1725
|
+
if (context.flags.json || context.flags.quiet) return;
|
|
1726
|
+
const settings = resolution.settings;
|
|
1727
|
+
const title = resolution.status === "created" ? `Created ${resolution.relativeConfigPath}` : `Using ${resolution.relativeConfigPath}`;
|
|
1728
|
+
context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [{
|
|
1729
|
+
label: "Build Command",
|
|
1730
|
+
value: settings.buildCommand ?? "none",
|
|
1731
|
+
origin: settings.buildCommandSource ?? void 0
|
|
1732
|
+
}, {
|
|
1733
|
+
label: "Output Directory",
|
|
1734
|
+
value: settings.outputDirectory,
|
|
1735
|
+
origin: settings.outputDirectorySource ?? void 0
|
|
1736
|
+
}]).join("\n")}\n\n`);
|
|
1737
|
+
}
|
|
1623
1738
|
function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
|
|
1624
1739
|
if (context.flags.json || context.flags.quiet) return;
|
|
1625
1740
|
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
@@ -1629,10 +1744,14 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1629
1744
|
framework: options.framework,
|
|
1630
1745
|
runtime: options.runtime
|
|
1631
1746
|
};
|
|
1747
|
+
maybeRenderDeploySettingsPreview(context, {
|
|
1748
|
+
framework: options.framework,
|
|
1749
|
+
runtime: options.runtime
|
|
1750
|
+
});
|
|
1632
1751
|
if (!await confirmPrompt({
|
|
1633
1752
|
input: context.runtime.stdin,
|
|
1634
1753
|
output: context.runtime.stderr,
|
|
1635
|
-
message: "Customize settings?",
|
|
1754
|
+
message: "Customize build settings?",
|
|
1636
1755
|
initialValue: false
|
|
1637
1756
|
})) return {
|
|
1638
1757
|
framework: options.framework,
|
|
@@ -1677,6 +1796,16 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1677
1796
|
runtime
|
|
1678
1797
|
};
|
|
1679
1798
|
}
|
|
1799
|
+
function maybeRenderDeploySettingsPreview(context, options) {
|
|
1800
|
+
if (context.flags.quiet || context.flags.json) return;
|
|
1801
|
+
context.output.stderr.write(`Detected ${options.framework.displayName}\n${renderDeploySettingsPreview(context.ui, [{
|
|
1802
|
+
key: "framework",
|
|
1803
|
+
value: options.framework.displayName
|
|
1804
|
+
}, {
|
|
1805
|
+
key: "runtime",
|
|
1806
|
+
value: `HTTP ${options.runtime.port}`
|
|
1807
|
+
}]).join("\n")}\n\n`);
|
|
1808
|
+
}
|
|
1680
1809
|
function frameworkDisplayName(framework) {
|
|
1681
1810
|
switch (framework) {
|
|
1682
1811
|
case "nextjs": return "Next.js";
|
|
@@ -1701,7 +1830,7 @@ function formatDeployDirectory(cwd) {
|
|
|
1701
1830
|
async function readCurrentWorkspaceId(context) {
|
|
1702
1831
|
const state = await context.stateStore.read();
|
|
1703
1832
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
1704
|
-
return (await readAuthState(context.runtime.env)).workspace?.id ?? null;
|
|
1833
|
+
return (await readAuthState(context.runtime.env, context.runtime.signal)).workspace?.id ?? null;
|
|
1705
1834
|
}
|
|
1706
1835
|
function normalizeBuildType(requestedBuildType) {
|
|
1707
1836
|
if (!requestedBuildType) return "auto";
|
|
@@ -1723,7 +1852,7 @@ function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
|
1723
1852
|
}
|
|
1724
1853
|
}
|
|
1725
1854
|
async function requireLocalBuildType(context, buildType, commandName) {
|
|
1726
|
-
const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
|
|
1855
|
+
const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType, context.runtime.signal);
|
|
1727
1856
|
if (resolvedBuildType) return resolvedBuildType;
|
|
1728
1857
|
throw usageError(`App ${commandName} 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 ${commandName} --build-type nextjs`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
|
|
1729
1858
|
}
|
|
@@ -1852,6 +1981,18 @@ function localResolutionPinStaleError() {
|
|
|
1852
1981
|
]
|
|
1853
1982
|
});
|
|
1854
1983
|
}
|
|
1984
|
+
function localPinReadErrorToDeployError(error) {
|
|
1985
|
+
return matchError(error, {
|
|
1986
|
+
LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
|
|
1987
|
+
LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
|
|
1988
|
+
LocalResolutionPinReadAbortedError: (error) => {
|
|
1989
|
+
throw error;
|
|
1990
|
+
},
|
|
1991
|
+
UnhandledException: (error) => {
|
|
1992
|
+
throw error;
|
|
1993
|
+
}
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1855
1996
|
function readDeployEnvOverride(context, name) {
|
|
1856
1997
|
const value = context.runtime.env[name]?.trim();
|
|
1857
1998
|
return value ? value : void 0;
|