@prisma/cli 3.0.0-dev.81.1 → 3.0.0-dev.83.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/token-storage.js +2 -2
- package/dist/cli.js +7 -7
- package/dist/cli2.js +3 -3
- package/dist/controllers/app-env.js +7 -6
- package/dist/controllers/app.js +140 -111
- package/dist/controllers/branch.js +1 -1
- package/dist/controllers/database.js +1 -1
- package/dist/controllers/project.js +4 -4
- package/dist/lib/app/branch-database-deploy.js +53 -42
- package/dist/lib/app/branch-database.js +90 -29
- package/dist/lib/app/preview-build.js +63 -16
- package/dist/lib/app/preview-provider.js +23 -19
- package/dist/lib/app/production-deploy-gate.js +1 -1
- package/dist/lib/auth/login.js +31 -24
- package/dist/lib/project/resolution.js +1 -1
- package/dist/lib/project/setup.js +3 -2
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/app.js +1 -1
- package/dist/presenters/branch.js +1 -1
- package/dist/presenters/database.js +2 -2
- package/dist/presenters/project.js +1 -1
- package/dist/shell/command-runner.js +37 -27
- package/dist/shell/help.js +30 -20
- package/dist/shell/runtime.js +1 -1
- package/dist/shell/ui.js +19 -2
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getAuthFilePath } from "../lib/auth/client.js";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { CredentialsStore } from "@prisma/credentials-store";
|
|
5
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { CredentialsStore } from "@prisma/credentials-store";
|
|
6
6
|
//#region src/adapters/token-storage.ts
|
|
7
7
|
function findLatestValidTokens(allCredentials) {
|
|
8
8
|
for (let i = allCredentials.length - 1; i >= 0; i -= 1) {
|
|
@@ -49,7 +49,7 @@ var FileTokenStorage = class {
|
|
|
49
49
|
const all = await this.credentialsStore.getCredentials();
|
|
50
50
|
this.signal?.throwIfAborted();
|
|
51
51
|
return findLatestValidTokens(all);
|
|
52
|
-
} catch (
|
|
52
|
+
} catch (_error) {
|
|
53
53
|
if (this.signal?.aborted) throw this.signal.reason;
|
|
54
54
|
return null;
|
|
55
55
|
}
|
package/dist/cli.js
CHANGED
|
@@ -3,22 +3,22 @@ import { runUpdateDiscoveryWorker } from "./shell/update-check.js";
|
|
|
3
3
|
import { runCli } from "./cli2.js";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
//#region src/bin.ts
|
|
6
|
-
if (process.env.PRISMA_CLI_RUN_UPDATE_CHECK_WORKER === "1")
|
|
6
|
+
if (process.env.PRISMA_CLI_RUN_UPDATE_CHECK_WORKER === "1") {
|
|
7
|
+
await runUpdateDiscoveryWorker();
|
|
7
8
|
process.exitCode = 0;
|
|
8
|
-
}
|
|
9
|
-
else {
|
|
9
|
+
} else {
|
|
10
10
|
const controller = new AbortController();
|
|
11
11
|
const abortCli = () => {
|
|
12
12
|
if (!controller.signal.aborted) controller.abort(new DOMException("Command canceled", "AbortError"));
|
|
13
13
|
};
|
|
14
14
|
process.once("SIGINT", abortCli);
|
|
15
15
|
process.once("SIGTERM", abortCli);
|
|
16
|
-
|
|
17
|
-
process.exitCode =
|
|
18
|
-
}
|
|
16
|
+
try {
|
|
17
|
+
process.exitCode = await runCli({ signal: controller.signal });
|
|
18
|
+
} finally {
|
|
19
19
|
process.off("SIGINT", abortCli);
|
|
20
20
|
process.off("SIGTERM", abortCli);
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
24
|
export {};
|
package/dist/cli2.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { CliError } from "./shell/errors.js";
|
|
2
|
-
import
|
|
3
|
-
import { formatUnexpectedError, writeHumanError, writeJsonError, writeJsonSuccess } from "./shell/output.js";
|
|
2
|
+
import "./shell/prompt.js";
|
|
4
3
|
import { attachCommandDescriptor } from "./shell/command-meta.js";
|
|
5
4
|
import { addCompactGlobalFlags } from "./shell/global-flags.js";
|
|
5
|
+
import { createShellUi } from "./shell/ui.js";
|
|
6
6
|
import { configureRuntimeCommand, createCommandContext } from "./shell/runtime.js";
|
|
7
|
-
import "./shell/
|
|
7
|
+
import { formatUnexpectedError, writeHumanError, writeJsonError, writeJsonSuccess } from "./shell/output.js";
|
|
8
8
|
import { createAppCommand } from "./commands/app/index.js";
|
|
9
9
|
import { createAuthCommand } from "./commands/auth/index.js";
|
|
10
10
|
import { createBranchCommand } from "./commands/branch/index.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
3
3
|
import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
|
|
4
4
|
import { readEnvFileAssignments } from "../lib/app/env-file.js";
|
|
5
|
-
import {
|
|
5
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
6
6
|
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
7
7
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
8
8
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
@@ -204,12 +204,13 @@ async function requireClientAndProject(context, explicitProject, commandName) {
|
|
|
204
204
|
const authState = await requireAuthenticatedAuthState(context);
|
|
205
205
|
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
206
206
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
207
|
-
|
|
207
|
+
const workspace = authState.workspace;
|
|
208
|
+
if (!workspace) throw workspaceRequiredError();
|
|
208
209
|
const targetResult = await resolveProjectTarget({
|
|
209
210
|
context,
|
|
210
|
-
workspace
|
|
211
|
+
workspace,
|
|
211
212
|
explicitProject,
|
|
212
|
-
listProjects: () => listRealWorkspaceProjects(client,
|
|
213
|
+
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
|
|
213
214
|
commandName
|
|
214
215
|
});
|
|
215
216
|
if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
|
|
@@ -218,7 +219,7 @@ async function requireClientAndProject(context, explicitProject, commandName) {
|
|
|
218
219
|
client,
|
|
219
220
|
projectId: target.project.id,
|
|
220
221
|
verboseContext: {
|
|
221
|
-
workspace
|
|
222
|
+
workspace,
|
|
222
223
|
project: target.project,
|
|
223
224
|
resolution: target.resolution
|
|
224
225
|
}
|
package/dist/controllers/app.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
2
2
|
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
3
3
|
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
4
|
+
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
4
5
|
import { renderCommandHeader } from "../shell/ui.js";
|
|
5
|
-
import { writeJsonEvent } from "../shell/output.js";
|
|
6
6
|
import { canPrompt } from "../shell/runtime.js";
|
|
7
|
-
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
8
|
-
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
9
|
-
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
|
-
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
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
7
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
8
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
16
9
|
import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
17
10
|
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
11
|
+
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
12
|
+
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
13
|
+
import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
|
|
14
|
+
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
15
|
+
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
16
|
+
import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
|
|
20
17
|
import { resolveOrCreatePreviewBuildSettings } from "../lib/app/preview-build-settings.js";
|
|
21
18
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
22
19
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
23
|
-
import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
|
|
24
20
|
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
25
21
|
import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
26
22
|
import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
|
|
27
|
-
import {
|
|
23
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
24
|
+
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
25
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
26
|
+
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
27
|
+
import { writeJsonEvent } from "../shell/output.js";
|
|
28
28
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
29
29
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
30
30
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
31
31
|
import { access, readFile } from "node:fs/promises";
|
|
32
32
|
import path from "node:path";
|
|
33
|
-
import open from "open";
|
|
34
33
|
import { Result, matchError } from "better-result";
|
|
34
|
+
import open from "open";
|
|
35
35
|
//#region src/controllers/app.ts
|
|
36
36
|
const DEPLOY_FRAMEWORKS = [
|
|
37
37
|
"nextjs",
|
|
@@ -910,51 +910,72 @@ async function confirmDomainRemoval(context, target, hostname) {
|
|
|
910
910
|
})) 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");
|
|
911
911
|
}
|
|
912
912
|
function domainCommandError(command, error, hostname) {
|
|
913
|
-
if (error instanceof PreviewDomainApiError)
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
913
|
+
if (error instanceof PreviewDomainApiError) return domainApiCommandError(command, error, hostname);
|
|
914
|
+
return new CliError({
|
|
915
|
+
code: "DEPLOY_FAILED",
|
|
916
|
+
domain: "app",
|
|
917
|
+
summary: `Custom domain ${command} failed`,
|
|
918
|
+
why: error instanceof Error ? error.message : String(error),
|
|
919
|
+
fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
|
|
920
|
+
debug: formatDebugDetails(error),
|
|
921
|
+
exitCode: 1,
|
|
922
|
+
nextSteps: [`prisma-cli app domain show ${hostname}`]
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
function domainApiCommandError(command, error, hostname) {
|
|
926
|
+
if (command === "add") return domainAddCommandError(error, hostname);
|
|
927
|
+
if (error.status === 404) return domainNotFoundError(hostname);
|
|
928
|
+
if (command === "retry" && error.status === 409) return domainRetryNotEligibleError(error, hostname);
|
|
929
|
+
return domainGenericCommandError(command, error, hostname);
|
|
930
|
+
}
|
|
931
|
+
function domainAddCommandError(error, hostname) {
|
|
932
|
+
if ((error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
|
|
933
|
+
if (error.status === 400) return new CliError({
|
|
934
|
+
code: "DOMAIN_HOSTNAME_INVALID",
|
|
935
|
+
domain: "app",
|
|
936
|
+
summary: `Invalid custom domain "${hostname}"`,
|
|
937
|
+
why: error.message,
|
|
938
|
+
fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
|
|
939
|
+
debug: formatDebugDetails(error),
|
|
940
|
+
exitCode: 2,
|
|
941
|
+
nextSteps: ["prisma-cli app domain add shop.acme.com"]
|
|
942
|
+
});
|
|
943
|
+
if (error.status === 429 || isDomainQuotaError(error)) return new CliError({
|
|
944
|
+
code: "DOMAIN_QUOTA_EXCEEDED",
|
|
945
|
+
domain: "app",
|
|
946
|
+
summary: "Custom domain quota exceeded",
|
|
947
|
+
why: error.message,
|
|
948
|
+
fix: "Remove an existing custom domain before adding another one.",
|
|
949
|
+
debug: formatDebugDetails(error),
|
|
950
|
+
exitCode: 1,
|
|
951
|
+
nextSteps: ["prisma-cli app domain remove <hostname>"]
|
|
952
|
+
});
|
|
953
|
+
if (error.status === 409) return domainAlreadyRegisteredError(hostname, error);
|
|
954
|
+
if (error.status === 422) return new CliError({
|
|
955
|
+
code: "NO_DEPLOYMENTS",
|
|
956
|
+
domain: "app",
|
|
957
|
+
summary: "Custom domain requires a live production deployment",
|
|
958
|
+
why: "The selected production app does not have a promoted version that can receive a custom domain.",
|
|
959
|
+
fix: "Deploy the app to the production branch, then rerun the domain command.",
|
|
960
|
+
debug: formatDebugDetails(error),
|
|
961
|
+
exitCode: 1,
|
|
962
|
+
nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
|
|
963
|
+
});
|
|
964
|
+
return domainGenericCommandError("add", error, hostname);
|
|
965
|
+
}
|
|
966
|
+
function domainRetryNotEligibleError(error, hostname) {
|
|
967
|
+
return new CliError({
|
|
968
|
+
code: "DOMAIN_RETRY_NOT_ELIGIBLE",
|
|
969
|
+
domain: "app",
|
|
970
|
+
summary: `Custom domain "${hostname}" is not eligible for retry`,
|
|
971
|
+
why: error.message,
|
|
972
|
+
fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
|
|
973
|
+
debug: formatDebugDetails(error),
|
|
974
|
+
exitCode: 1,
|
|
975
|
+
nextSteps: [`prisma-cli app domain show ${hostname}`]
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function domainGenericCommandError(command, error, hostname) {
|
|
958
979
|
return new CliError({
|
|
959
980
|
code: "DEPLOY_FAILED",
|
|
960
981
|
domain: "app",
|
|
@@ -1356,14 +1377,14 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1356
1377
|
};
|
|
1357
1378
|
}
|
|
1358
1379
|
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1359
|
-
const
|
|
1360
|
-
if (!
|
|
1380
|
+
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1381
|
+
if (!workspace) throw workspaceRequiredError();
|
|
1361
1382
|
const resolvedResult = await resolveProjectTarget({
|
|
1362
1383
|
context,
|
|
1363
|
-
workspace
|
|
1384
|
+
workspace,
|
|
1364
1385
|
explicitProject,
|
|
1365
1386
|
envProjectId: options?.envProjectId,
|
|
1366
|
-
listProjects: () => listRealWorkspaceProjects(client,
|
|
1387
|
+
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
|
|
1367
1388
|
commandName: options?.commandName
|
|
1368
1389
|
});
|
|
1369
1390
|
if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
|
|
@@ -1382,8 +1403,16 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1382
1403
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
1383
1404
|
if (!workspace) throw workspaceRequiredError();
|
|
1384
1405
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1385
|
-
|
|
1386
|
-
|
|
1406
|
+
return withRemoteDeployBranch(provider, await resolveDeployProjectSetup(context, provider, workspace, await listRealWorkspaceProjects(client, workspace, context.runtime.signal), explicitProject, options), branch, context.runtime.signal);
|
|
1407
|
+
}
|
|
1408
|
+
async function resolveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options) {
|
|
1409
|
+
const selected = await resolveNonInteractiveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options);
|
|
1410
|
+
if (selected) return selected;
|
|
1411
|
+
if (canPrompt(context) && !context.flags.yes) return resolveInteractiveDeployProjectSetup(context, provider, workspace, projects);
|
|
1412
|
+
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1413
|
+
}
|
|
1414
|
+
async function resolveNonInteractiveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options) {
|
|
1415
|
+
if (explicitProject) return {
|
|
1387
1416
|
workspace,
|
|
1388
1417
|
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1389
1418
|
resolution: {
|
|
@@ -1392,11 +1421,11 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1392
1421
|
targetNameSource: "explicit"
|
|
1393
1422
|
},
|
|
1394
1423
|
localPinAction: "linked"
|
|
1395
|
-
}
|
|
1424
|
+
};
|
|
1396
1425
|
if (options.createProjectName) {
|
|
1397
1426
|
const projectName = options.createProjectName.trim();
|
|
1398
1427
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1399
|
-
return
|
|
1428
|
+
return {
|
|
1400
1429
|
workspace,
|
|
1401
1430
|
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1402
1431
|
resolution: {
|
|
@@ -1405,12 +1434,12 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1405
1434
|
targetNameSource: "explicit"
|
|
1406
1435
|
},
|
|
1407
1436
|
localPinAction: "created"
|
|
1408
|
-
}
|
|
1437
|
+
};
|
|
1409
1438
|
}
|
|
1410
1439
|
if (options.envProjectId) {
|
|
1411
1440
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1412
1441
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
1413
|
-
return
|
|
1442
|
+
return {
|
|
1414
1443
|
workspace,
|
|
1415
1444
|
project: toProjectSummary(project),
|
|
1416
1445
|
resolution: {
|
|
@@ -1418,14 +1447,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1418
1447
|
targetName: options.envProjectId,
|
|
1419
1448
|
targetNameSource: "env"
|
|
1420
1449
|
}
|
|
1421
|
-
}
|
|
1450
|
+
};
|
|
1422
1451
|
}
|
|
1423
1452
|
const localPin = options.localPin;
|
|
1424
1453
|
if (localPin.kind === "present") {
|
|
1425
1454
|
if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
|
|
1426
1455
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
1427
1456
|
if (!project) throw localResolutionPinStaleError();
|
|
1428
|
-
return
|
|
1457
|
+
return {
|
|
1429
1458
|
workspace,
|
|
1430
1459
|
project: toProjectSummary(project),
|
|
1431
1460
|
resolution: {
|
|
@@ -1433,10 +1462,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1433
1462
|
targetName: project.name,
|
|
1434
1463
|
targetNameSource: "local-pin"
|
|
1435
1464
|
}
|
|
1436
|
-
}
|
|
1465
|
+
};
|
|
1437
1466
|
}
|
|
1438
1467
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
1439
|
-
if (platformMapping && platformMapping.workspace.id === workspace.id) return
|
|
1468
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return {
|
|
1440
1469
|
workspace,
|
|
1441
1470
|
project: toProjectSummary(platformMapping),
|
|
1442
1471
|
resolution: {
|
|
@@ -1444,9 +1473,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1444
1473
|
targetName: platformMapping.name,
|
|
1445
1474
|
targetNameSource: "platform-mapping"
|
|
1446
1475
|
}
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1476
|
+
};
|
|
1477
|
+
return null;
|
|
1450
1478
|
}
|
|
1451
1479
|
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1452
1480
|
const setup = await promptForProjectSetupChoice({
|
|
@@ -1890,41 +1918,7 @@ function deployFailedError(summary, error, nextSteps) {
|
|
|
1890
1918
|
function appDeployFailedError(error, progress) {
|
|
1891
1919
|
const why = error instanceof Error ? error.message : String(error);
|
|
1892
1920
|
const debug = formatDebugDetails(error);
|
|
1893
|
-
if (progress.buildStarted && !progress.buildCompleted)
|
|
1894
|
-
const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
|
|
1895
|
-
const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
|
|
1896
|
-
const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
|
|
1897
|
-
const nextActions = standaloneOutputFailure ? [{
|
|
1898
|
-
kind: "edit-file",
|
|
1899
|
-
journey: "deploy-app",
|
|
1900
|
-
label: "Add Next.js standalone output",
|
|
1901
|
-
reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
|
|
1902
|
-
}, {
|
|
1903
|
-
kind: "run-command",
|
|
1904
|
-
journey: "deploy-app",
|
|
1905
|
-
label: "Rerun deploy",
|
|
1906
|
-
command: "prisma-cli app deploy"
|
|
1907
|
-
}] : [];
|
|
1908
|
-
return new CliError({
|
|
1909
|
-
code: "BUILD_FAILED",
|
|
1910
|
-
domain: "app",
|
|
1911
|
-
summary: "Build failed locally.",
|
|
1912
|
-
why,
|
|
1913
|
-
fix,
|
|
1914
|
-
debug,
|
|
1915
|
-
meta: { phase: "build" },
|
|
1916
|
-
humanLines: [
|
|
1917
|
-
"Build failed locally.",
|
|
1918
|
-
"",
|
|
1919
|
-
`✗ Built ${why}`,
|
|
1920
|
-
"",
|
|
1921
|
-
`Fix: ${fix}`
|
|
1922
|
-
],
|
|
1923
|
-
exitCode: 1,
|
|
1924
|
-
nextSteps,
|
|
1925
|
-
nextActions
|
|
1926
|
-
});
|
|
1927
|
-
}
|
|
1921
|
+
if (progress.buildStarted && !progress.buildCompleted) return appBuildFailedError(why, debug);
|
|
1928
1922
|
if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
|
|
1929
1923
|
const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
|
|
1930
1924
|
const recoveryLines = progress.versionId ? ["See what happened", `prisma-cli app logs --deployment ${progress.versionId}`] : ["Fix", "Retry the command, or rerun with --trace for more detailed diagnostics."];
|
|
@@ -1965,6 +1959,41 @@ function appDeployFailedError(error, progress) {
|
|
|
1965
1959
|
nextSteps: []
|
|
1966
1960
|
});
|
|
1967
1961
|
}
|
|
1962
|
+
function appBuildFailedError(why, debug) {
|
|
1963
|
+
const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
|
|
1964
|
+
const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
|
|
1965
|
+
const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
|
|
1966
|
+
const nextActions = standaloneOutputFailure ? [{
|
|
1967
|
+
kind: "edit-file",
|
|
1968
|
+
journey: "deploy-app",
|
|
1969
|
+
label: "Add Next.js standalone output",
|
|
1970
|
+
reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
|
|
1971
|
+
}, {
|
|
1972
|
+
kind: "run-command",
|
|
1973
|
+
journey: "deploy-app",
|
|
1974
|
+
label: "Rerun deploy",
|
|
1975
|
+
command: "prisma-cli app deploy"
|
|
1976
|
+
}] : [];
|
|
1977
|
+
return new CliError({
|
|
1978
|
+
code: "BUILD_FAILED",
|
|
1979
|
+
domain: "app",
|
|
1980
|
+
summary: "Build failed locally.",
|
|
1981
|
+
why,
|
|
1982
|
+
fix,
|
|
1983
|
+
debug,
|
|
1984
|
+
meta: { phase: "build" },
|
|
1985
|
+
humanLines: [
|
|
1986
|
+
"Build failed locally.",
|
|
1987
|
+
"",
|
|
1988
|
+
`✗ Built ${why}`,
|
|
1989
|
+
"",
|
|
1990
|
+
`Fix: ${fix}`
|
|
1991
|
+
],
|
|
1992
|
+
exitCode: 1,
|
|
1993
|
+
nextSteps,
|
|
1994
|
+
nextActions
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1968
1997
|
function localResolutionPinStaleError() {
|
|
1969
1998
|
return new CliError({
|
|
1970
1999
|
code: "LOCAL_STATE_STALE",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
|
-
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
3
2
|
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
3
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
4
4
|
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
|
|
5
5
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
6
6
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
|
-
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
3
2
|
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
3
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
4
4
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
5
5
|
import { listFixtureWorkspaceProjects, listRealWorkspaceProjects } from "./project.js";
|
|
6
6
|
import { createManagementDatabaseProvider, normalizeConnection, normalizeDatabase } from "../lib/database/provider.js";
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
2
|
import { renderSummaryLine } from "../shell/ui.js";
|
|
3
3
|
import { canPrompt } from "../shell/runtime.js";
|
|
4
|
-
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
5
|
-
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
6
4
|
import { readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
5
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
7
6
|
import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectResolutionErrorToCliError, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
8
7
|
import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
9
|
-
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
10
8
|
import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
9
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
10
|
+
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
11
11
|
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
|
|
12
12
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
13
13
|
import { parseGitHubRepositoryUrl, readGitOriginRemote } from "../adapters/git.js";
|
|
14
14
|
import { createProjectUseCases } from "../use-cases/project.js";
|
|
15
|
-
import open from "open";
|
|
16
15
|
import { matchError } from "better-result";
|
|
16
|
+
import open from "open";
|
|
17
17
|
//#region src/controllers/project.ts
|
|
18
18
|
const GITHUB_INSTALL_POLL_INTERVAL_MS = 2e3;
|
|
19
19
|
const GITHUB_INSTALL_POLL_TIMEOUT_MS = 12e4;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CliError, usageError } from "../../shell/errors.js";
|
|
2
|
+
import { confirmPrompt } from "../../shell/prompt.js";
|
|
2
3
|
import { renderSummaryLine } from "../../shell/ui.js";
|
|
3
4
|
import { canPrompt } from "../../shell/runtime.js";
|
|
4
|
-
import { confirmPrompt } from "../../shell/prompt.js";
|
|
5
5
|
import { formatCommandArgument } from "../../shell/command-arguments.js";
|
|
6
6
|
import "../project/setup.js";
|
|
7
7
|
import { hasBranchDatabaseSignal, inspectBranchDatabaseSignal, runBranchDatabaseSchemaSetup } from "./branch-database.js";
|
|
@@ -9,6 +9,22 @@ import path from "node:path";
|
|
|
9
9
|
//#region src/lib/app/branch-database-deploy.ts
|
|
10
10
|
async function maybeSetupBranchDatabase(context, provider, projectId, branch, options) {
|
|
11
11
|
if (options.db === false) return emptyBranchDatabaseSetupOutcome();
|
|
12
|
+
const preflight = branchDatabasePreflight(branch, options);
|
|
13
|
+
if (preflight) return preflight;
|
|
14
|
+
const envState = await inspectBranchDatabaseEnv(provider, projectId, branch, context.runtime.signal);
|
|
15
|
+
const existingEnvOutcome = existingBranchDatabaseEnvOutcome(context, branch, getTargetDatabaseEnvVarKeys(envState), envState, options.db);
|
|
16
|
+
if (existingEnvOutcome) return existingEnvOutcome;
|
|
17
|
+
const localSignal = await inspectBranchDatabaseSignal(context.runtime.cwd, context.runtime.signal);
|
|
18
|
+
if (localSignal.unsupportedSchema) {
|
|
19
|
+
if (options.db === true) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch, context);
|
|
20
|
+
return emptyBranchDatabaseSetupOutcome();
|
|
21
|
+
}
|
|
22
|
+
const promptOutcome = await branchDatabasePromptOutcome(context, branch, localSignal, envState, options.db);
|
|
23
|
+
if (promptOutcome) return promptOutcome;
|
|
24
|
+
if (options.db === true && !canPrompt(context) && !context.flags.yes) throw nonInteractiveDatabaseSetupRequiresYesError(branch);
|
|
25
|
+
return setupBranchDatabase(context, provider, projectId, branch, localSignal, envState);
|
|
26
|
+
}
|
|
27
|
+
function branchDatabasePreflight(branch, options) {
|
|
12
28
|
if (hasProvidedDatabaseEnvVars(options.providedEnvVars)) {
|
|
13
29
|
if (options.db === true) throw usageError("Database setup cannot be combined with provided database env vars", "The deploy command received --db and a DATABASE_URL or DIRECT_URL value from --env.", "Remove the --env database value to let --db create and wire a database, or remove --db to deploy with the provided value.", ["prisma-cli app deploy --db", "prisma-cli app deploy --env DATABASE_URL=postgresql://example"], "app");
|
|
14
30
|
return emptyBranchDatabaseSetupOutcome();
|
|
@@ -17,46 +33,40 @@ async function maybeSetupBranchDatabase(context, provider, projectId, branch, op
|
|
|
17
33
|
if (options.db === true) throw productionDatabaseSetupAfterFirstDeployError();
|
|
18
34
|
return emptyBranchDatabaseSetupOutcome();
|
|
19
35
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function existingBranchDatabaseEnvOutcome(context, branch, targetEnvVars, envState, requested) {
|
|
39
|
+
if (!hasExistingDatabaseEnvForTarget(branch, envState)) return null;
|
|
40
|
+
const warning = requested === true ? existingDatabaseEnvWarning(branch, targetEnvVars) : null;
|
|
41
|
+
if (warning) emitBranchDatabaseWarning(context, warning);
|
|
42
|
+
return {
|
|
43
|
+
result: requested === true ? {
|
|
44
|
+
status: "skipped",
|
|
45
|
+
reason: existingDatabaseEnvReason(branch),
|
|
46
|
+
envVars: targetEnvVars,
|
|
47
|
+
schema: null
|
|
48
|
+
} : void 0,
|
|
49
|
+
warnings: warning ? [warning] : []
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async function branchDatabasePromptOutcome(context, branch, localSignal, envState, requested) {
|
|
53
|
+
if (requested === true) return null;
|
|
54
|
+
if (!(hasBranchDatabaseSignal(localSignal) || Boolean(envState.inheritedPreviewDatabaseUrl))) return emptyBranchDatabaseSetupOutcome();
|
|
55
|
+
if (!canPrompt(context) || context.flags.yes) {
|
|
56
|
+
const warning = databasePromptSuppressedWarning(branch);
|
|
57
|
+
emitBranchDatabaseWarning(context, warning);
|
|
25
58
|
return {
|
|
26
|
-
result:
|
|
27
|
-
|
|
28
|
-
reason: existingDatabaseEnvReason(branch),
|
|
29
|
-
envVars: targetEnvVars,
|
|
30
|
-
schema: null
|
|
31
|
-
} : void 0,
|
|
32
|
-
warnings: warning ? [warning] : []
|
|
59
|
+
result: void 0,
|
|
60
|
+
warnings: [warning]
|
|
33
61
|
};
|
|
34
62
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!hasSignal) return emptyBranchDatabaseSetupOutcome();
|
|
43
|
-
if (!canPrompt(context) || context.flags.yes) {
|
|
44
|
-
const warning = databasePromptSuppressedWarning(branch);
|
|
45
|
-
emitBranchDatabaseWarning(context, warning);
|
|
46
|
-
return {
|
|
47
|
-
result: void 0,
|
|
48
|
-
warnings: [warning]
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
maybeRenderBranchDatabaseSignal(context, branch, localSignal, envState);
|
|
52
|
-
if (!await confirmPrompt({
|
|
53
|
-
input: context.runtime.stdin,
|
|
54
|
-
output: context.output.stderr,
|
|
55
|
-
message: databasePromptMessage(branch),
|
|
56
|
-
initialValue: false
|
|
57
|
-
})) return emptyBranchDatabaseSetupOutcome();
|
|
58
|
-
} else if (!canPrompt(context) && !context.flags.yes) throw nonInteractiveDatabaseSetupRequiresYesError(branch);
|
|
59
|
-
return setupBranchDatabase(context, provider, projectId, branch, localSignal, envState);
|
|
63
|
+
maybeRenderBranchDatabaseSignal(context, branch, localSignal, envState);
|
|
64
|
+
return await confirmPrompt({
|
|
65
|
+
input: context.runtime.stdin,
|
|
66
|
+
output: context.output.stderr,
|
|
67
|
+
message: databasePromptMessage(branch),
|
|
68
|
+
initialValue: false
|
|
69
|
+
}) ? null : emptyBranchDatabaseSetupOutcome();
|
|
60
70
|
}
|
|
61
71
|
async function setupBranchDatabase(context, provider, projectId, branch, signal, envState) {
|
|
62
72
|
emitBranchDatabaseProgress(context, "pending", "Creating database");
|
|
@@ -73,15 +83,16 @@ async function setupBranchDatabase(context, provider, projectId, branch, signal,
|
|
|
73
83
|
let schemaSetup = null;
|
|
74
84
|
const warnings = [];
|
|
75
85
|
let skippedSchemaWarning = null;
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
const schema = signal.schema;
|
|
87
|
+
if (schema) {
|
|
88
|
+
emitBranchDatabaseProgress(context, "pending", `Applying database schema with ${formatSchemaSetupCommand(schema.command)}`);
|
|
78
89
|
schemaSetup = await runBranchDatabaseSchemaSetup({
|
|
79
90
|
context,
|
|
80
|
-
schema
|
|
91
|
+
schema,
|
|
81
92
|
databaseUrl: database.databaseUrl,
|
|
82
93
|
directUrl: database.directUrl
|
|
83
94
|
}).catch((error) => {
|
|
84
|
-
throw schemaSetupFailedError(error,
|
|
95
|
+
throw schemaSetupFailedError(error, schema, branch, context.runtime.cwd);
|
|
85
96
|
});
|
|
86
97
|
emitBranchDatabaseProgress(context, "success", "Applied database schema");
|
|
87
98
|
} else skippedSchemaWarning = "No supported Prisma schema source was found. Database env vars were created, but schema setup was skipped.";
|