@prisma/cli 3.0.0-dev.39.1 → 3.0.0-dev.41.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/commands/app/index.js +3 -4
- package/dist/controllers/app-env.js +6 -8
- package/dist/controllers/app.js +81 -56
- package/dist/controllers/project.js +28 -14
- package/dist/lib/project/resolution.js +85 -96
- package/dist/lib/project/setup.js +1 -1
- package/dist/presenters/project.js +39 -4
- package/dist/shell/command-meta.js +3 -2
- package/package.json +1 -1
|
@@ -62,13 +62,13 @@ function createDeployCommand(runtime) {
|
|
|
62
62
|
command.addOption(new Option("--app <name>", "App name")).addOption(new Option("--project <id-or-name>", "Project id or name")).addOption(new Option("--create-project <name>", "Create and link a new Project before deploying")).addOption(new Option("--branch <name>", "Branch name")).addOption(new Option("--framework <name>", "Framework to deploy").choices([
|
|
63
63
|
"nextjs",
|
|
64
64
|
"hono",
|
|
65
|
-
"tanstack-start"
|
|
66
|
-
|
|
65
|
+
"tanstack-start",
|
|
66
|
+
"bun"
|
|
67
|
+
])).addOption(new Option("--entry <path>", "Entrypoint path for Bun deploys")).addOption(new Option("--http-port <port>", "HTTP port override for the deployed app")).addOption(new Option("--env <name=value>", "Environment variable").argParser(collectRepeatableValues));
|
|
67
68
|
addGlobalFlags(command);
|
|
68
69
|
command.action(async (options) => {
|
|
69
70
|
const appName = options.app;
|
|
70
71
|
const entry = options.entry;
|
|
71
|
-
const buildType = options.buildType;
|
|
72
72
|
const branchName = options.branch;
|
|
73
73
|
const framework = options.framework;
|
|
74
74
|
const httpPort = options.httpPort;
|
|
@@ -80,7 +80,6 @@ function createDeployCommand(runtime) {
|
|
|
80
80
|
createProjectName,
|
|
81
81
|
branchName,
|
|
82
82
|
entrypoint: entry,
|
|
83
|
-
buildType,
|
|
84
83
|
framework,
|
|
85
84
|
httpPort,
|
|
86
85
|
envAssignments
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
2
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
3
3
|
import { resolveProjectTarget } from "../lib/project/resolution.js";
|
|
4
|
-
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
5
4
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
6
5
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
7
6
|
import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
|
|
@@ -19,7 +18,7 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
19
18
|
command: "add"
|
|
20
19
|
});
|
|
21
20
|
if (!scope) throw usageError(`prisma-cli project env add requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env add ${key}=${value} --role production`], "app");
|
|
22
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
|
|
21
|
+
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env add");
|
|
23
22
|
const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: true });
|
|
24
23
|
if (await findVariableByNaturalKey(client, projectId, key, resolved)) throw new CliError({
|
|
25
24
|
code: "ENV_VARIABLE_ALREADY_EXISTS",
|
|
@@ -70,7 +69,7 @@ async function runEnvUpdate(context, rawAssignment, flags) {
|
|
|
70
69
|
command: "update"
|
|
71
70
|
});
|
|
72
71
|
if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env update ${key}=${value} --role production`], "app");
|
|
73
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
|
|
72
|
+
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env update");
|
|
74
73
|
const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: false });
|
|
75
74
|
const existing = await findVariableByNaturalKey(client, projectId, key, resolved);
|
|
76
75
|
if (!existing) throw new CliError({
|
|
@@ -103,7 +102,7 @@ async function runEnvList(context, flags) {
|
|
|
103
102
|
requireExplicit: false,
|
|
104
103
|
command: "list"
|
|
105
104
|
}) ?? defaultRoleScope();
|
|
106
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
|
|
105
|
+
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env list");
|
|
107
106
|
const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: false });
|
|
108
107
|
const variables = await listVariables(client, projectId, resolved);
|
|
109
108
|
return {
|
|
@@ -124,7 +123,7 @@ async function runEnvRemove(context, key, flags) {
|
|
|
124
123
|
command: "remove"
|
|
125
124
|
});
|
|
126
125
|
if (!scope) throw usageError("prisma-cli project env remove requires --role or --branch", "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env remove ${key} --role production`], "app");
|
|
127
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
|
|
126
|
+
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env remove");
|
|
128
127
|
const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: false });
|
|
129
128
|
const existing = await findVariableByNaturalKey(client, projectId, key, resolved);
|
|
130
129
|
if (!existing) throw new CliError({
|
|
@@ -149,7 +148,7 @@ async function runEnvRemove(context, key, flags) {
|
|
|
149
148
|
nextSteps: []
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
|
-
async function requireClientAndProject(context, explicitProject) {
|
|
151
|
+
async function requireClientAndProject(context, explicitProject, commandName) {
|
|
153
152
|
const authState = await requireAuthenticatedAuthState(context);
|
|
154
153
|
const client = await requireComputeAuth(context.runtime.env);
|
|
155
154
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
@@ -161,8 +160,7 @@ async function requireClientAndProject(context, explicitProject) {
|
|
|
161
160
|
workspace: authState.workspace,
|
|
162
161
|
explicitProject,
|
|
163
162
|
listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
|
|
164
|
-
|
|
165
|
-
remember: true
|
|
163
|
+
commandName
|
|
166
164
|
})).project.id
|
|
167
165
|
};
|
|
168
166
|
}
|
package/dist/controllers/app.js
CHANGED
|
@@ -9,10 +9,10 @@ import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
|
9
9
|
import { readAuthState } from "../lib/auth/auth-ops.js";
|
|
10
10
|
import { parseEnvAssignments } from "../lib/app/env-vars.js";
|
|
11
11
|
import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
|
|
12
|
-
import { readBunPackageJson } from "../lib/app/bun-project.js";
|
|
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
|
-
import { inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
15
14
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
15
|
+
import { inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
16
16
|
import { bindProjectToDirectory, formatCommandArgument, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
17
17
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
18
18
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
@@ -29,8 +29,10 @@ import open from "open";
|
|
|
29
29
|
const DEPLOY_FRAMEWORKS = [
|
|
30
30
|
"nextjs",
|
|
31
31
|
"hono",
|
|
32
|
-
"tanstack-start"
|
|
32
|
+
"tanstack-start",
|
|
33
|
+
"bun"
|
|
33
34
|
];
|
|
35
|
+
const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
|
|
34
36
|
const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
|
|
35
37
|
const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
|
|
36
38
|
const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
|
|
@@ -105,15 +107,12 @@ async function runAppDeploy(context, appName, options) {
|
|
|
105
107
|
const skipLocalPin = Boolean(envProjectId || options?.projectRef || options?.createProjectName);
|
|
106
108
|
const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
|
|
107
109
|
if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
|
|
108
|
-
const
|
|
110
|
+
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
109
111
|
if (options?.httpPort) parseDeployHttpPort(options.httpPort);
|
|
110
112
|
assertSupportedEntrypointForRequestedDeployShape({
|
|
111
113
|
requestedFramework: options?.framework,
|
|
112
|
-
requestedBuildType: options?.buildType,
|
|
113
|
-
explicitBuildType,
|
|
114
114
|
entrypoint: options?.entrypoint
|
|
115
115
|
});
|
|
116
|
-
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
117
116
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
118
117
|
branch,
|
|
119
118
|
createProjectName: options?.createProjectName,
|
|
@@ -128,8 +127,7 @@ async function runAppDeploy(context, appName, options) {
|
|
|
128
127
|
}
|
|
129
128
|
let framework = await resolveDeployFramework(context, {
|
|
130
129
|
requestedFramework: options?.framework,
|
|
131
|
-
|
|
132
|
-
explicitBuildType
|
|
130
|
+
entrypoint: options?.entrypoint
|
|
133
131
|
});
|
|
134
132
|
let runtime = resolveDeployRuntime(options?.httpPort, framework);
|
|
135
133
|
assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
|
|
@@ -151,13 +149,14 @@ async function runAppDeploy(context, appName, options) {
|
|
|
151
149
|
runtime,
|
|
152
150
|
firstDeploy: selectedApp.firstDeploy,
|
|
153
151
|
explicitFramework: Boolean(options?.framework),
|
|
154
|
-
|
|
152
|
+
explicitEntrypoint: Boolean(options?.entrypoint),
|
|
155
153
|
explicitHttpPort: Boolean(options?.httpPort)
|
|
156
154
|
});
|
|
157
155
|
framework = customized.framework;
|
|
158
156
|
runtime = customized.runtime;
|
|
159
157
|
const buildType = framework.buildType;
|
|
160
158
|
assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
|
|
159
|
+
const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint);
|
|
161
160
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
162
161
|
const progressState = createPreviewDeployProgressState();
|
|
163
162
|
const deployStartedAt = Date.now();
|
|
@@ -168,7 +167,7 @@ async function runAppDeploy(context, appName, options) {
|
|
|
168
167
|
appId: selectedApp.appId,
|
|
169
168
|
appName: selectedApp.appName,
|
|
170
169
|
region: selectedApp.region,
|
|
171
|
-
entrypoint
|
|
170
|
+
entrypoint,
|
|
172
171
|
buildType,
|
|
173
172
|
portMapping,
|
|
174
173
|
envVars,
|
|
@@ -204,7 +203,7 @@ async function runAppDeploy(context, appName, options) {
|
|
|
204
203
|
}
|
|
205
204
|
async function runAppListDeploys(context, appName, projectRef) {
|
|
206
205
|
ensurePreviewAppMode(context);
|
|
207
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
206
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
|
|
208
207
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
209
208
|
if (!selectedApp) return {
|
|
210
209
|
command: "app.list-deploys",
|
|
@@ -241,7 +240,7 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
241
240
|
}
|
|
242
241
|
async function runAppShow(context, appName, projectRef) {
|
|
243
242
|
ensurePreviewAppMode(context);
|
|
244
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
243
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
|
|
245
244
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
246
245
|
if (!selectedApp) return {
|
|
247
246
|
command: "app.show",
|
|
@@ -317,7 +316,7 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
317
316
|
}
|
|
318
317
|
async function runAppOpen(context, appName, projectRef) {
|
|
319
318
|
ensurePreviewAppMode(context);
|
|
320
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
319
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
|
|
321
320
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
322
321
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
323
322
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
@@ -351,7 +350,7 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
351
350
|
}
|
|
352
351
|
async function runAppDomainAdd(context, hostname, options) {
|
|
353
352
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
354
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
353
|
+
const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
|
|
355
354
|
const added = await target.provider.addDomain({
|
|
356
355
|
appId: target.app.id,
|
|
357
356
|
hostname: normalizedHostname
|
|
@@ -371,7 +370,7 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
371
370
|
}
|
|
372
371
|
async function runAppDomainShow(context, hostname, options) {
|
|
373
372
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
374
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
373
|
+
const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
|
|
375
374
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
|
|
376
375
|
const detail = await target.provider.showDomain(domain.id).catch((error) => {
|
|
377
376
|
throw domainCommandError("show", error, normalizedHostname);
|
|
@@ -388,7 +387,7 @@ async function runAppDomainShow(context, hostname, options) {
|
|
|
388
387
|
}
|
|
389
388
|
async function runAppDomainRemove(context, hostname, options) {
|
|
390
389
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
391
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
390
|
+
const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
|
|
392
391
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
|
|
393
392
|
await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
|
|
394
393
|
await target.provider.removeDomain(domain.id).catch((error) => {
|
|
@@ -407,7 +406,7 @@ async function runAppDomainRemove(context, hostname, options) {
|
|
|
407
406
|
}
|
|
408
407
|
async function runAppDomainRetry(context, hostname, options) {
|
|
409
408
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
410
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
409
|
+
const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
|
|
411
410
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
|
|
412
411
|
const retried = await target.provider.retryDomain(domain.id).catch((error) => {
|
|
413
412
|
throw domainCommandError("retry", error, normalizedHostname);
|
|
@@ -425,7 +424,7 @@ async function runAppDomainRetry(context, hostname, options) {
|
|
|
425
424
|
async function runAppDomainWait(context, hostname, options) {
|
|
426
425
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
427
426
|
const timeoutMs = parseDomainWaitTimeout(options?.timeout);
|
|
428
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
427
|
+
const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
|
|
429
428
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
|
|
430
429
|
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
|
|
431
430
|
`app domain wait -> Waiting for ${normalizedHostname} to become active.`,
|
|
@@ -477,7 +476,7 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
477
476
|
}
|
|
478
477
|
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
479
478
|
ensurePreviewAppMode(context);
|
|
480
|
-
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
479
|
+
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
|
|
481
480
|
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
482
481
|
if (!context.flags.json && !context.flags.quiet) {
|
|
483
482
|
const lines = renderCommandHeader(context.ui, {
|
|
@@ -601,7 +600,7 @@ function writeLogRecord(context, record) {
|
|
|
601
600
|
}
|
|
602
601
|
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
603
602
|
ensurePreviewAppMode(context);
|
|
604
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
603
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
|
|
605
604
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
606
605
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
607
606
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -641,7 +640,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
641
640
|
}
|
|
642
641
|
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
643
642
|
ensurePreviewAppMode(context);
|
|
644
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
643
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
|
|
645
644
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
646
645
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
647
646
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -683,7 +682,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
683
682
|
}
|
|
684
683
|
async function runAppRemove(context, appName, projectRef) {
|
|
685
684
|
ensurePreviewAppMode(context);
|
|
686
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
685
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
|
|
687
686
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
688
687
|
await confirmAppRemoval(context, selectedApp);
|
|
689
688
|
const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
|
|
@@ -704,7 +703,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
704
703
|
nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
|
|
705
704
|
};
|
|
706
705
|
}
|
|
707
|
-
async function resolveAppDomainTarget(context, options) {
|
|
706
|
+
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
708
707
|
ensurePreviewAppMode(context);
|
|
709
708
|
const branch = resolveDomainBranch(options?.branchName);
|
|
710
709
|
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
@@ -718,13 +717,10 @@ async function resolveAppDomainTarget(context, options) {
|
|
|
718
717
|
});
|
|
719
718
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
720
719
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
721
|
-
const
|
|
722
|
-
const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
|
|
723
|
-
if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
|
|
724
|
-
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
720
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
725
721
|
branch,
|
|
726
|
-
|
|
727
|
-
|
|
722
|
+
commandName,
|
|
723
|
+
envProjectId
|
|
728
724
|
});
|
|
729
725
|
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
730
726
|
explicitAppName: options?.appName,
|
|
@@ -1219,9 +1215,9 @@ async function listApps(context, provider, projectId, branchName) {
|
|
|
1219
1215
|
domain: "project",
|
|
1220
1216
|
summary: "Project not found",
|
|
1221
1217
|
why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
|
|
1222
|
-
fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect
|
|
1218
|
+
fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect this directory's binding.",
|
|
1223
1219
|
exitCode: 1,
|
|
1224
|
-
nextSteps: ["prisma-cli project show", "prisma-cli
|
|
1220
|
+
nextSteps: ["prisma-cli project show", "prisma-cli project link <id-or-name>"]
|
|
1225
1221
|
});
|
|
1226
1222
|
throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
|
|
1227
1223
|
});
|
|
@@ -1281,8 +1277,9 @@ async function resolveProjectContext(context, client, explicitProject, options)
|
|
|
1281
1277
|
context,
|
|
1282
1278
|
workspace: authState.workspace,
|
|
1283
1279
|
explicitProject,
|
|
1280
|
+
envProjectId: options?.envProjectId,
|
|
1284
1281
|
listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
|
|
1285
|
-
|
|
1282
|
+
commandName: options?.commandName
|
|
1286
1283
|
});
|
|
1287
1284
|
const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1288
1285
|
return {
|
|
@@ -1505,15 +1502,12 @@ async function resolveGitHeadPath(gitPath) {
|
|
|
1505
1502
|
}
|
|
1506
1503
|
async function resolveDeployFramework(context, options) {
|
|
1507
1504
|
if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
|
|
1508
|
-
if (options.
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
annotation: "set by --build-type"
|
|
1515
|
-
};
|
|
1516
|
-
}
|
|
1505
|
+
if (options.entrypoint) return {
|
|
1506
|
+
key: "bun",
|
|
1507
|
+
buildType: "bun",
|
|
1508
|
+
displayName: "Bun",
|
|
1509
|
+
annotation: "set by --entry"
|
|
1510
|
+
};
|
|
1517
1511
|
const detected = await detectDeployFramework(context.runtime.cwd);
|
|
1518
1512
|
if (detected) return detected;
|
|
1519
1513
|
throw frameworkNotDetectedError(context.runtime.cwd);
|
|
@@ -1529,12 +1523,22 @@ function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
|
1529
1523
|
};
|
|
1530
1524
|
}
|
|
1531
1525
|
function assertSupportedEntrypointForRequestedDeployShape(options) {
|
|
1532
|
-
if (options.requestedFramework)
|
|
1533
|
-
|
|
1526
|
+
if (!options.requestedFramework) return;
|
|
1527
|
+
assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
|
|
1528
|
+
}
|
|
1529
|
+
async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
|
|
1530
|
+
if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
|
|
1531
|
+
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
|
|
1532
|
+
if (packageEntrypoint) return packageEntrypoint;
|
|
1533
|
+
if (framework.key !== "hono") return;
|
|
1534
|
+
const defaultEntrypoint = "src/index.ts";
|
|
1535
|
+
try {
|
|
1536
|
+
await access(path.join(cwd, defaultEntrypoint));
|
|
1537
|
+
return defaultEntrypoint;
|
|
1538
|
+
} catch (error) {
|
|
1539
|
+
if (error.code !== "ENOENT") throw error;
|
|
1534
1540
|
return;
|
|
1535
1541
|
}
|
|
1536
|
-
if (!options.explicitBuildType) return;
|
|
1537
|
-
assertSupportedEntrypoint(normalizeBuildType(options.requestedBuildType), options.entrypoint, "deploy");
|
|
1538
1542
|
}
|
|
1539
1543
|
async function detectDeployFramework(cwd) {
|
|
1540
1544
|
const packageJson = await readBunPackageJson(cwd);
|
|
@@ -1551,7 +1555,7 @@ async function detectDeployFramework(cwd) {
|
|
|
1551
1555
|
displayName: "Hono",
|
|
1552
1556
|
annotation: "detected from package.json"
|
|
1553
1557
|
};
|
|
1554
|
-
if (
|
|
1558
|
+
if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
|
|
1555
1559
|
key: "tanstack-start",
|
|
1556
1560
|
buildType: "tanstack-start",
|
|
1557
1561
|
displayName: "TanStack Start",
|
|
@@ -1564,7 +1568,8 @@ async function detectNextConfig(cwd) {
|
|
|
1564
1568
|
"next.config.js",
|
|
1565
1569
|
"next.config.mjs",
|
|
1566
1570
|
"next.config.cjs",
|
|
1567
|
-
"next.config.ts"
|
|
1571
|
+
"next.config.ts",
|
|
1572
|
+
"next.config.mts"
|
|
1568
1573
|
]) {
|
|
1569
1574
|
const filePath = path.join(cwd, candidate);
|
|
1570
1575
|
try {
|
|
@@ -1585,6 +1590,9 @@ async function detectNextConfig(cwd) {
|
|
|
1585
1590
|
function hasPackageDependency(packageJson, dependencyName) {
|
|
1586
1591
|
return hasDependency(packageJson?.dependencies, dependencyName) || hasDependency(packageJson?.devDependencies, dependencyName);
|
|
1587
1592
|
}
|
|
1593
|
+
function hasAnyPackageDependency(packageJson, dependencyNames) {
|
|
1594
|
+
return dependencyNames.some((dependencyName) => hasPackageDependency(packageJson, dependencyName));
|
|
1595
|
+
}
|
|
1588
1596
|
function hasDependency(dependencies, dependencyName) {
|
|
1589
1597
|
return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
|
|
1590
1598
|
}
|
|
@@ -1604,9 +1612,16 @@ function frameworkFromUserFacingValue(value, annotation) {
|
|
|
1604
1612
|
displayName: "Hono",
|
|
1605
1613
|
annotation
|
|
1606
1614
|
};
|
|
1615
|
+
case "bun": return {
|
|
1616
|
+
key: "bun",
|
|
1617
|
+
buildType: "bun",
|
|
1618
|
+
displayName: "Bun",
|
|
1619
|
+
annotation
|
|
1620
|
+
};
|
|
1607
1621
|
case "tanstack":
|
|
1608
1622
|
case "tanstack-start":
|
|
1609
|
-
case "@tanstack/start":
|
|
1623
|
+
case "@tanstack/react-start":
|
|
1624
|
+
case "@tanstack/solid-start": return {
|
|
1610
1625
|
key: "tanstack-start",
|
|
1611
1626
|
buildType: "tanstack-start",
|
|
1612
1627
|
displayName: "TanStack Start",
|
|
@@ -1616,19 +1631,21 @@ function frameworkFromUserFacingValue(value, annotation) {
|
|
|
1616
1631
|
}
|
|
1617
1632
|
}
|
|
1618
1633
|
function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
1619
|
-
const supported = "Next.js, Hono, TanStack Start";
|
|
1634
|
+
const supported = "Next.js, Hono, TanStack Start, Bun";
|
|
1620
1635
|
const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
|
|
1621
1636
|
return new CliError({
|
|
1622
1637
|
code: "FRAMEWORK_NOT_DETECTED",
|
|
1623
1638
|
domain: "app",
|
|
1624
1639
|
summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
|
|
1625
1640
|
why: `Supported Beta frameworks: ${supported}.`,
|
|
1626
|
-
fix: "Add one of these frameworks as a dependency,
|
|
1641
|
+
fix: "Add one of these frameworks as a dependency, pass --framework <nextjs|hono|tanstack-start|bun>, or pass --entry <path> for a Bun app.",
|
|
1627
1642
|
exitCode: 2,
|
|
1628
1643
|
nextSteps: [
|
|
1629
1644
|
"prisma-cli app deploy --framework nextjs",
|
|
1630
1645
|
"prisma-cli app deploy --framework hono",
|
|
1631
|
-
"prisma-cli app deploy --framework tanstack-start"
|
|
1646
|
+
"prisma-cli app deploy --framework tanstack-start",
|
|
1647
|
+
"prisma-cli app deploy --framework bun --entry server.ts",
|
|
1648
|
+
"prisma-cli app deploy --entry server.ts"
|
|
1632
1649
|
]
|
|
1633
1650
|
});
|
|
1634
1651
|
}
|
|
@@ -1643,7 +1660,7 @@ function maybeRenderProjectLinked(context, directory, projectName, localPinPath)
|
|
|
1643
1660
|
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
1644
1661
|
}
|
|
1645
1662
|
async function maybeCustomizeDeploySettings(context, options) {
|
|
1646
|
-
if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.
|
|
1663
|
+
if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitEntrypoint || options.explicitHttpPort || !canPrompt(context)) return {
|
|
1647
1664
|
framework: options.framework,
|
|
1648
1665
|
runtime: options.runtime
|
|
1649
1666
|
};
|
|
@@ -1700,6 +1717,7 @@ function frameworkDisplayName(framework) {
|
|
|
1700
1717
|
case "nextjs": return "Next.js";
|
|
1701
1718
|
case "hono": return "Hono";
|
|
1702
1719
|
case "tanstack-start": return "TanStack Start";
|
|
1720
|
+
case "bun": return "Bun";
|
|
1703
1721
|
}
|
|
1704
1722
|
}
|
|
1705
1723
|
function validateDeployHttpPortText(value) {
|
|
@@ -1734,7 +1752,10 @@ function getBuildTypeExamples(commandName) {
|
|
|
1734
1752
|
});
|
|
1735
1753
|
}
|
|
1736
1754
|
function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
|
|
1737
|
-
if (buildType !== "auto" && buildType !== "bun" && entrypoint)
|
|
1755
|
+
if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
|
|
1756
|
+
if (commandName === "deploy") throw usageError(`App deploy does not accept --entry with ${formatBuildTypeName(buildType)}`, `${formatBuildTypeName(buildType)} apps derive their runtime entrypoint from build output.`, "Remove --entry, or use --framework bun when you want to target a Bun entrypoint directly.", [`prisma-cli app deploy --framework ${buildType}`, "prisma-cli app deploy --framework bun --entry server.ts"], "app");
|
|
1757
|
+
throw usageError(`App ${commandName} does not accept --entry with --build-type ${buildType}`, `${formatBuildTypeName(buildType)} apps do not use an entrypoint flag in the current preview.`, `Remove --entry, or rerun prisma-cli app ${commandName} with --build-type bun when you want to target a Bun entrypoint directly.`, [`prisma-cli app ${commandName} --build-type ${buildType}`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
|
|
1758
|
+
}
|
|
1738
1759
|
}
|
|
1739
1760
|
async function requireLocalBuildType(context, buildType, commandName) {
|
|
1740
1761
|
const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
|
|
@@ -1839,10 +1860,14 @@ function localResolutionPinStaleError() {
|
|
|
1839
1860
|
domain: "project",
|
|
1840
1861
|
summary: "Local project binding is stale",
|
|
1841
1862
|
why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
1842
|
-
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}
|
|
1863
|
+
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
|
|
1843
1864
|
meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
|
|
1844
1865
|
exitCode: 1,
|
|
1845
|
-
nextSteps: [
|
|
1866
|
+
nextSteps: [
|
|
1867
|
+
"prisma-cli project list",
|
|
1868
|
+
"prisma-cli project link <id-or-name>",
|
|
1869
|
+
"prisma-cli app deploy --project <id-or-name>"
|
|
1870
|
+
]
|
|
1846
1871
|
});
|
|
1847
1872
|
}
|
|
1848
1873
|
function readDeployEnvOverride(context, name) {
|
|
@@ -2,8 +2,8 @@ import { CliError, authRequiredError, featureUnavailableError, usageError, works
|
|
|
2
2
|
import { renderSummaryLine } from "../shell/ui.js";
|
|
3
3
|
import { canPrompt } from "../shell/runtime.js";
|
|
4
4
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
5
|
-
import { resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
6
|
-
import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup } from "../lib/project/setup.js";
|
|
5
|
+
import { inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
6
|
+
import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
7
7
|
import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
8
8
|
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
|
|
9
9
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
@@ -94,7 +94,7 @@ async function runGitConnect(context, gitUrl, options = {}) {
|
|
|
94
94
|
if (isRealMode(context)) {
|
|
95
95
|
const client = await requireComputeAuth(context.runtime.env);
|
|
96
96
|
if (!client) throw authRequiredError();
|
|
97
|
-
const target = await
|
|
97
|
+
const target = await resolveRequiredProjectInRealMode(context, workspace, options.project, "git connect");
|
|
98
98
|
const repository = await resolveRepositoryForConnect(context, gitUrl);
|
|
99
99
|
const api = client;
|
|
100
100
|
const existing = await readFirstSourceRepository(api, target.project.id);
|
|
@@ -129,7 +129,7 @@ async function runGitConnect(context, gitUrl, options = {}) {
|
|
|
129
129
|
nextSteps: []
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
-
const target = await
|
|
132
|
+
const target = await resolveRequiredProjectInFixtureMode(context, workspace, options.project, "git connect");
|
|
133
133
|
const repository = await resolveRepositoryForConnect(context, gitUrl);
|
|
134
134
|
const existingConnection = await context.stateStore.readRepositoryConnection(target.project.id);
|
|
135
135
|
if (existingConnection) {
|
|
@@ -162,7 +162,7 @@ async function runGitDisconnect(context, options = {}) {
|
|
|
162
162
|
if (isRealMode(context)) {
|
|
163
163
|
const client = await requireComputeAuth(context.runtime.env);
|
|
164
164
|
if (!client) throw authRequiredError();
|
|
165
|
-
const target = await
|
|
165
|
+
const target = await resolveRequiredProjectInRealMode(context, workspace, options.project, "git disconnect");
|
|
166
166
|
const api = client;
|
|
167
167
|
const existing = await readFirstSourceRepository(api, target.project.id);
|
|
168
168
|
if (!existing) throw repoNotConnectedError();
|
|
@@ -178,7 +178,7 @@ async function runGitDisconnect(context, options = {}) {
|
|
|
178
178
|
nextSteps: []
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
|
-
const target = await
|
|
181
|
+
const target = await resolveRequiredProjectInFixtureMode(context, workspace, options.project, "git disconnect");
|
|
182
182
|
const existingConnection = await context.stateStore.readRepositoryConnection(target.project.id);
|
|
183
183
|
if (!existingConnection) throw repoNotConnectedError();
|
|
184
184
|
await context.stateStore.clearRepositoryConnection(target.project.id);
|
|
@@ -193,6 +193,17 @@ async function runGitDisconnect(context, options = {}) {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
async function resolveProjectShowInRealMode(context, workspace, explicitProject) {
|
|
196
|
+
const client = await requireComputeAuth(context.runtime.env);
|
|
197
|
+
if (!client) throw authRequiredError();
|
|
198
|
+
return inspectProjectBinding({
|
|
199
|
+
context,
|
|
200
|
+
workspace,
|
|
201
|
+
explicitProject,
|
|
202
|
+
listProjects: () => listRealWorkspaceProjects(client, workspace),
|
|
203
|
+
commandName: "project show"
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function resolveRequiredProjectInRealMode(context, workspace, explicitProject, commandName) {
|
|
196
207
|
const client = await requireComputeAuth(context.runtime.env);
|
|
197
208
|
if (!client) throw authRequiredError();
|
|
198
209
|
return resolveProjectTarget({
|
|
@@ -200,7 +211,7 @@ async function resolveProjectShowInRealMode(context, workspace, explicitProject)
|
|
|
200
211
|
workspace,
|
|
201
212
|
explicitProject,
|
|
202
213
|
listProjects: () => listRealWorkspaceProjects(client, workspace),
|
|
203
|
-
|
|
214
|
+
commandName
|
|
204
215
|
});
|
|
205
216
|
}
|
|
206
217
|
async function listRealProjectsForLink(context, workspace) {
|
|
@@ -209,12 +220,21 @@ async function listRealProjectsForLink(context, workspace) {
|
|
|
209
220
|
return listRealWorkspaceProjects(client, workspace);
|
|
210
221
|
}
|
|
211
222
|
async function resolveProjectShowInFixtureMode(context, workspace, explicitProject) {
|
|
223
|
+
return inspectProjectBinding({
|
|
224
|
+
context,
|
|
225
|
+
workspace,
|
|
226
|
+
explicitProject,
|
|
227
|
+
listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
|
|
228
|
+
commandName: "project show"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
async function resolveRequiredProjectInFixtureMode(context, workspace, explicitProject, commandName) {
|
|
212
232
|
return resolveProjectTarget({
|
|
213
233
|
context,
|
|
214
234
|
workspace,
|
|
215
235
|
explicitProject,
|
|
216
236
|
listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
|
|
217
|
-
|
|
237
|
+
commandName
|
|
218
238
|
});
|
|
219
239
|
}
|
|
220
240
|
async function listRealWorkspaceProjects(client, workspace) {
|
|
@@ -539,11 +559,5 @@ function repoConnectionFixForStatus(status) {
|
|
|
539
559
|
if (status === 422) return "Make sure the GitHub App installation has access to this repository.";
|
|
540
560
|
return "Re-run with --trace for the underlying API response details.";
|
|
541
561
|
}
|
|
542
|
-
function toProjectSummary(project) {
|
|
543
|
-
return {
|
|
544
|
-
id: project.id,
|
|
545
|
-
name: project.name
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
562
|
//#endregion
|
|
549
563
|
export { listRealWorkspaceProjects, runGitConnect, runGitDisconnect, runProjectCreate, runProjectLink, runProjectList, runProjectShow };
|
|
@@ -1,73 +1,31 @@
|
|
|
1
1
|
import { CliError } from "../../shell/errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "./local-pin.js";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
//#region src/lib/project/resolution.ts
|
|
6
6
|
async function resolveProjectTarget(options) {
|
|
7
7
|
const projects = await options.listProjects();
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const target = await resolveBoundProjectTarget(options, projects, { allowEnvProjectId: true });
|
|
9
|
+
if (target) return target;
|
|
10
|
+
throw await projectSetupRequiredError({
|
|
11
|
+
cwd: options.context.runtime.cwd,
|
|
12
|
+
projects,
|
|
13
|
+
commandName: options.commandName
|
|
12
14
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (rememberedResult.target) return rememberedResult.target;
|
|
19
|
-
staleRemembered = rememberedResult.stale;
|
|
20
|
-
}
|
|
21
|
-
const packageName = inferredName.source === "package-name" ? inferredName.name : null;
|
|
22
|
-
if (packageName) {
|
|
23
|
-
const matches = projects.filter((project) => projectMatchesPackageName(project, packageName));
|
|
24
|
-
if (matches.length === 1) return rememberIfRequested(options, matches[0], "package-name", {
|
|
25
|
-
targetName: packageName,
|
|
26
|
-
targetNameSource: "package-name"
|
|
27
|
-
});
|
|
28
|
-
if (matches.length > 1) return resolveAmbiguousProject(options, matches, packageName, "package-name");
|
|
29
|
-
}
|
|
30
|
-
if (options.allowCreate && options.createProject) {
|
|
31
|
-
if (inferredName.name) {
|
|
32
|
-
const existing = projects.filter((project) => projectMatchesPackageName(project, inferredName.name));
|
|
33
|
-
if (existing.length === 1) return rememberIfRequested(options, existing[0], inferredName.source, {
|
|
34
|
-
targetName: inferredName.name,
|
|
35
|
-
targetNameSource: inferredName.source
|
|
36
|
-
});
|
|
37
|
-
if (existing.length > 1) return resolveAmbiguousProject(options, existing, inferredName.name, inferredName.source);
|
|
38
|
-
return rememberIfRequested(options, await options.createProject(inferredName.name), "created", {
|
|
39
|
-
targetName: inferredName.name,
|
|
40
|
-
targetNameSource: inferredName.source
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (options.prompt && canPrompt(options.context) && projects.length > 0) return rememberIfRequested(options, await options.prompt.select({
|
|
45
|
-
message: "Select a project",
|
|
46
|
-
choices: sortProjects(projects).map((project) => ({
|
|
47
|
-
label: `${project.name} (${project.id})`,
|
|
48
|
-
value: project
|
|
49
|
-
}))
|
|
50
|
-
}), "prompt");
|
|
51
|
-
if (staleRemembered && projects.length > 1) throw localStateStaleError();
|
|
52
|
-
throw projectUnresolvedError();
|
|
53
|
-
}
|
|
54
|
-
async function resolveRememberedProject(options, projects) {
|
|
55
|
-
const remembered = await options.context.stateStore.readRememberedProject(options.workspace.id);
|
|
56
|
-
if (!remembered) return {
|
|
57
|
-
target: null,
|
|
58
|
-
stale: false
|
|
59
|
-
};
|
|
60
|
-
const matched = projects.find((project) => project.id === remembered.id);
|
|
61
|
-
if (!matched) return {
|
|
62
|
-
target: null,
|
|
63
|
-
stale: true
|
|
64
|
-
};
|
|
15
|
+
}
|
|
16
|
+
async function inspectProjectBinding(options) {
|
|
17
|
+
const projects = await options.listProjects();
|
|
18
|
+
const target = await resolveBoundProjectTarget(options, projects, { allowEnvProjectId: false });
|
|
19
|
+
if (target) return target;
|
|
65
20
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
21
|
+
workspace: options.workspace,
|
|
22
|
+
project: null,
|
|
23
|
+
resolution: { projectSource: "unbound" },
|
|
24
|
+
...await buildProjectSetupSuggestion({
|
|
25
|
+
cwd: options.context.runtime.cwd,
|
|
26
|
+
projects,
|
|
27
|
+
commandName: options.commandName ?? "project show"
|
|
28
|
+
})
|
|
71
29
|
};
|
|
72
30
|
}
|
|
73
31
|
function projectNotFoundError(projectRef, workspace) {
|
|
@@ -99,26 +57,39 @@ function projectAmbiguousError(projectRef, matches) {
|
|
|
99
57
|
nextSteps
|
|
100
58
|
});
|
|
101
59
|
}
|
|
102
|
-
function
|
|
60
|
+
function localStateStaleError() {
|
|
103
61
|
return new CliError({
|
|
104
|
-
code: "
|
|
62
|
+
code: "LOCAL_STATE_STALE",
|
|
105
63
|
domain: "project",
|
|
106
|
-
summary: "
|
|
107
|
-
why:
|
|
108
|
-
fix:
|
|
64
|
+
summary: "Local project binding is stale",
|
|
65
|
+
why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
66
|
+
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
|
|
67
|
+
meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
|
|
109
68
|
exitCode: 1,
|
|
110
|
-
nextSteps: ["prisma-cli project list", "prisma-cli project
|
|
69
|
+
nextSteps: ["prisma-cli project list", "prisma-cli project link <id-or-name>"]
|
|
111
70
|
});
|
|
112
71
|
}
|
|
113
|
-
function
|
|
72
|
+
async function buildProjectSetupSuggestion(options) {
|
|
73
|
+
const suggestedName = await inferTargetName(options.cwd);
|
|
74
|
+
const candidates = sortProjects(options.projects.filter((project) => projectMatchesSuggestedName(project, suggestedName.name))).map(toProjectSummary);
|
|
75
|
+
return {
|
|
76
|
+
suggestedProjectName: suggestedName.name,
|
|
77
|
+
suggestedProjectNameSource: suggestedName.source,
|
|
78
|
+
candidates,
|
|
79
|
+
recoveryCommands: buildProjectRecoveryCommands(options.commandName)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function projectSetupRequiredError(options) {
|
|
83
|
+
const suggestion = await buildProjectSetupSuggestion(options);
|
|
114
84
|
return new CliError({
|
|
115
|
-
code: "
|
|
85
|
+
code: "PROJECT_SETUP_REQUIRED",
|
|
116
86
|
domain: "project",
|
|
117
|
-
summary: "
|
|
118
|
-
why:
|
|
119
|
-
fix: "
|
|
87
|
+
summary: "Choose a Project before running this command",
|
|
88
|
+
why: `This directory is not linked to a Prisma Project, and ${options.commandName ? `prisma-cli ${options.commandName}` : "this command"} will not choose one from package or directory names.`,
|
|
89
|
+
fix: "Link the directory to an existing Project, or pass --project <id-or-name> for this command.",
|
|
90
|
+
meta: { ...suggestion },
|
|
120
91
|
exitCode: 1,
|
|
121
|
-
nextSteps: ["prisma-cli project list"]
|
|
92
|
+
nextSteps: ["prisma-cli project list", ...suggestion.recoveryCommands]
|
|
122
93
|
});
|
|
123
94
|
}
|
|
124
95
|
async function readPackageName(cwd) {
|
|
@@ -157,33 +128,46 @@ function resolveExplicitProject(projectRef, projects, workspace) {
|
|
|
157
128
|
if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
|
|
158
129
|
throw projectNotFoundError(projectRef, workspace);
|
|
159
130
|
}
|
|
160
|
-
function
|
|
161
|
-
|
|
162
|
-
message: "Select a project",
|
|
163
|
-
choices: sortProjects(matches).map((project) => ({
|
|
164
|
-
label: `${project.name} (${project.id})`,
|
|
165
|
-
value: project
|
|
166
|
-
}))
|
|
167
|
-
}).then((selected) => rememberIfRequested(options, selected, "prompt", {
|
|
168
|
-
targetName: projectRef,
|
|
169
|
-
targetNameSource
|
|
170
|
-
}));
|
|
171
|
-
throw projectAmbiguousError(projectRef, matches);
|
|
172
|
-
}
|
|
173
|
-
function projectMatchesPackageName(project, packageName) {
|
|
174
|
-
return project.id === packageName || project.name === packageName || project.slug === packageName;
|
|
131
|
+
function projectMatchesSuggestedName(project, suggestedName) {
|
|
132
|
+
return project.id === suggestedName || project.name === suggestedName || project.slug === suggestedName;
|
|
175
133
|
}
|
|
176
134
|
async function resolveDurablePlatformMapping() {
|
|
177
135
|
return null;
|
|
178
136
|
}
|
|
179
|
-
async function
|
|
180
|
-
if (options.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
workspaceId: options.workspace.id
|
|
137
|
+
async function resolveBoundProjectTarget(options, projects, settings) {
|
|
138
|
+
if (options.explicitProject) return resolvedTarget(options.workspace, resolveExplicitProject(options.explicitProject, projects, options.workspace), "explicit", {
|
|
139
|
+
targetName: options.explicitProject,
|
|
140
|
+
targetNameSource: "explicit"
|
|
184
141
|
});
|
|
142
|
+
if (settings.allowEnvProjectId && options.envProjectId) {
|
|
143
|
+
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
144
|
+
if (!project) throw projectNotFoundError(options.envProjectId, options.workspace);
|
|
145
|
+
return resolvedTarget(options.workspace, project, "env", {
|
|
146
|
+
targetName: options.envProjectId,
|
|
147
|
+
targetNameSource: "env"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const localPin = await readLocalResolutionPin(options.context.runtime.cwd);
|
|
151
|
+
if (localPin.kind === "invalid") throw localStateStaleError();
|
|
152
|
+
if (localPin.kind === "present") {
|
|
153
|
+
if (localPin.pin.workspaceId !== options.workspace.id) throw localStateStaleError();
|
|
154
|
+
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
155
|
+
if (!project) throw localStateStaleError();
|
|
156
|
+
return resolvedTarget(options.workspace, project, "local-pin", {
|
|
157
|
+
targetName: project.name,
|
|
158
|
+
targetNameSource: "local-pin"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const platformMapping = await resolveDurablePlatformMapping();
|
|
162
|
+
if (platformMapping && platformMapping.workspace.id === options.workspace.id) return resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
|
|
163
|
+
targetName: platformMapping.name,
|
|
164
|
+
targetNameSource: "platform-mapping"
|
|
165
|
+
});
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
function resolvedTarget(workspace, project, projectSource, resolutionDetails) {
|
|
185
169
|
return {
|
|
186
|
-
workspace
|
|
170
|
+
workspace,
|
|
187
171
|
project: toProjectSummary(project),
|
|
188
172
|
resolution: {
|
|
189
173
|
projectSource,
|
|
@@ -191,6 +175,11 @@ async function rememberIfRequested(options, project, projectSource, resolutionDe
|
|
|
191
175
|
}
|
|
192
176
|
};
|
|
193
177
|
}
|
|
178
|
+
function buildProjectRecoveryCommands(commandName) {
|
|
179
|
+
const commands = ["prisma-cli project link <id-or-name>"];
|
|
180
|
+
if (commandName) commands.push(`prisma-cli ${commandName} --project <id-or-name>`);
|
|
181
|
+
return commands;
|
|
182
|
+
}
|
|
194
183
|
function toProjectSummary(project) {
|
|
195
184
|
return {
|
|
196
185
|
id: project.id,
|
|
@@ -198,4 +187,4 @@ function toProjectSummary(project) {
|
|
|
198
187
|
};
|
|
199
188
|
}
|
|
200
189
|
//#endregion
|
|
201
|
-
export { inferTargetName, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
|
190
|
+
export { inferTargetName, inspectProjectBinding, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError, usageError } from "../../shell/errors.js";
|
|
2
|
-
import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
|
|
3
2
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
|
|
3
|
+
import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
|
|
4
4
|
//#region src/lib/project/setup.ts
|
|
5
5
|
function isValidProjectSetupName(projectName) {
|
|
6
6
|
return projectName.trim().length > 0;
|
|
@@ -30,8 +30,35 @@ function serializeProjectList(result) {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
function renderProjectShow(context, descriptor, result) {
|
|
33
|
+
if (result.project === null) return renderShow({
|
|
34
|
+
title: "No Project linked to this directory.",
|
|
35
|
+
descriptor,
|
|
36
|
+
fields: [
|
|
37
|
+
{
|
|
38
|
+
key: "workspace",
|
|
39
|
+
value: result.workspace.name
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "project",
|
|
43
|
+
value: "unbound",
|
|
44
|
+
tone: "warning"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: "suggested",
|
|
48
|
+
value: `${result.suggestedProjectName} (${formatSuggestionSource(result.suggestedProjectNameSource)})`
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: "match",
|
|
52
|
+
value: formatCandidateList(result.candidates)
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: "next",
|
|
56
|
+
value: result.recoveryCommands[0] ?? "prisma-cli project link <id-or-name>"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}, context.ui);
|
|
33
60
|
return renderShow({
|
|
34
|
-
title: "Showing
|
|
61
|
+
title: "Showing this directory's Project binding.",
|
|
35
62
|
descriptor,
|
|
36
63
|
fields: [
|
|
37
64
|
{
|
|
@@ -117,13 +144,21 @@ function formatProjectSource(source) {
|
|
|
117
144
|
case "env": return "environment";
|
|
118
145
|
case "local-pin": return "local pin";
|
|
119
146
|
case "platform-mapping": return "platform mapping";
|
|
120
|
-
case "remembered-local": return "remembered local context";
|
|
121
|
-
case "package-name": return "package name";
|
|
122
|
-
case "directory-name": return "directory name";
|
|
123
147
|
case "created": return "created";
|
|
124
148
|
case "prompt": return "prompt";
|
|
149
|
+
case "unbound": return "unbound";
|
|
125
150
|
}
|
|
126
151
|
}
|
|
152
|
+
function formatSuggestionSource(source) {
|
|
153
|
+
switch (source) {
|
|
154
|
+
case "package-name": return "package name";
|
|
155
|
+
case "directory-name": return "directory name";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function formatCandidateList(candidates) {
|
|
159
|
+
if (candidates.length === 0) return "none";
|
|
160
|
+
return candidates.map((project) => project.name).join(", ");
|
|
161
|
+
}
|
|
127
162
|
function formatGitConnectionDetail(status) {
|
|
128
163
|
switch (status) {
|
|
129
164
|
case "active": return "GitHub branch automation is active for this project.";
|
|
@@ -95,7 +95,7 @@ const DESCRIPTORS = [
|
|
|
95
95
|
"project",
|
|
96
96
|
"show"
|
|
97
97
|
],
|
|
98
|
-
description: "Show
|
|
98
|
+
description: "Show this directory's Project binding",
|
|
99
99
|
examples: ["prisma-cli project show", "prisma-cli project show --project proj_123 --json"]
|
|
100
100
|
},
|
|
101
101
|
{
|
|
@@ -212,7 +212,8 @@ const DESCRIPTORS = [
|
|
|
212
212
|
"prisma-cli app deploy --app my-app --env DATABASE_URL=postgresql://example",
|
|
213
213
|
"prisma-cli app deploy --app my-app --framework nextjs --http-port 3000",
|
|
214
214
|
"prisma-cli app deploy --branch feat-login --framework hono",
|
|
215
|
-
"pnpm dlx skills@latest add prisma/prisma-cli/skills#cli-v<cli-version> --all"
|
|
215
|
+
"pnpm dlx skills@latest add prisma/prisma-cli/skills#cli-v<cli-version> --all",
|
|
216
|
+
"prisma-cli app deploy --framework bun --entry src/server.ts"
|
|
216
217
|
]
|
|
217
218
|
},
|
|
218
219
|
{
|