@prisma/cli 3.0.0-dev.40.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/controllers/app-env.js +6 -8
- package/dist/controllers/app.js +27 -25
- 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 +1 -1
- package/package.json +1 -1
|
@@ -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
|
@@ -11,8 +11,8 @@ import { parseEnvAssignments } from "../lib/app/env-vars.js";
|
|
|
11
11
|
import { renderDeployOutputRows } 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
|
-
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";
|
|
@@ -203,7 +203,7 @@ async function runAppDeploy(context, appName, options) {
|
|
|
203
203
|
}
|
|
204
204
|
async function runAppListDeploys(context, appName, projectRef) {
|
|
205
205
|
ensurePreviewAppMode(context);
|
|
206
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
206
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
|
|
207
207
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
208
208
|
if (!selectedApp) return {
|
|
209
209
|
command: "app.list-deploys",
|
|
@@ -240,7 +240,7 @@ async function runAppListDeploys(context, appName, projectRef) {
|
|
|
240
240
|
}
|
|
241
241
|
async function runAppShow(context, appName, projectRef) {
|
|
242
242
|
ensurePreviewAppMode(context);
|
|
243
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
243
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
|
|
244
244
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
245
245
|
if (!selectedApp) return {
|
|
246
246
|
command: "app.show",
|
|
@@ -316,7 +316,7 @@ async function runAppShowDeploy(context, deploymentId) {
|
|
|
316
316
|
}
|
|
317
317
|
async function runAppOpen(context, appName, projectRef) {
|
|
318
318
|
ensurePreviewAppMode(context);
|
|
319
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
319
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
|
|
320
320
|
const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
|
|
321
321
|
if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
|
|
322
322
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
@@ -350,7 +350,7 @@ async function runAppOpen(context, appName, projectRef) {
|
|
|
350
350
|
}
|
|
351
351
|
async function runAppDomainAdd(context, hostname, options) {
|
|
352
352
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
353
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
353
|
+
const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
|
|
354
354
|
const added = await target.provider.addDomain({
|
|
355
355
|
appId: target.app.id,
|
|
356
356
|
hostname: normalizedHostname
|
|
@@ -370,7 +370,7 @@ async function runAppDomainAdd(context, hostname, options) {
|
|
|
370
370
|
}
|
|
371
371
|
async function runAppDomainShow(context, hostname, options) {
|
|
372
372
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
373
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
373
|
+
const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
|
|
374
374
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
|
|
375
375
|
const detail = await target.provider.showDomain(domain.id).catch((error) => {
|
|
376
376
|
throw domainCommandError("show", error, normalizedHostname);
|
|
@@ -387,7 +387,7 @@ async function runAppDomainShow(context, hostname, options) {
|
|
|
387
387
|
}
|
|
388
388
|
async function runAppDomainRemove(context, hostname, options) {
|
|
389
389
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
390
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
390
|
+
const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
|
|
391
391
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
|
|
392
392
|
await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
|
|
393
393
|
await target.provider.removeDomain(domain.id).catch((error) => {
|
|
@@ -406,7 +406,7 @@ async function runAppDomainRemove(context, hostname, options) {
|
|
|
406
406
|
}
|
|
407
407
|
async function runAppDomainRetry(context, hostname, options) {
|
|
408
408
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
409
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
409
|
+
const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
|
|
410
410
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
|
|
411
411
|
const retried = await target.provider.retryDomain(domain.id).catch((error) => {
|
|
412
412
|
throw domainCommandError("retry", error, normalizedHostname);
|
|
@@ -424,7 +424,7 @@ async function runAppDomainRetry(context, hostname, options) {
|
|
|
424
424
|
async function runAppDomainWait(context, hostname, options) {
|
|
425
425
|
const normalizedHostname = normalizeDomainHostname(hostname);
|
|
426
426
|
const timeoutMs = parseDomainWaitTimeout(options?.timeout);
|
|
427
|
-
const target = await resolveAppDomainTarget(context, options);
|
|
427
|
+
const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
|
|
428
428
|
const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
|
|
429
429
|
if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
|
|
430
430
|
`app domain wait -> Waiting for ${normalizedHostname} to become active.`,
|
|
@@ -476,7 +476,7 @@ async function runAppDomainWait(context, hostname, options) {
|
|
|
476
476
|
}
|
|
477
477
|
async function runAppLogs(context, appName, deploymentId, projectRef) {
|
|
478
478
|
ensurePreviewAppMode(context);
|
|
479
|
-
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
479
|
+
const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
|
|
480
480
|
const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
|
|
481
481
|
if (!context.flags.json && !context.flags.quiet) {
|
|
482
482
|
const lines = renderCommandHeader(context.ui, {
|
|
@@ -600,7 +600,7 @@ function writeLogRecord(context, record) {
|
|
|
600
600
|
}
|
|
601
601
|
async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
602
602
|
ensurePreviewAppMode(context);
|
|
603
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
603
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
|
|
604
604
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
|
|
605
605
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
606
606
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -640,7 +640,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
|
|
|
640
640
|
}
|
|
641
641
|
async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
642
642
|
ensurePreviewAppMode(context);
|
|
643
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
643
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
|
|
644
644
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
|
|
645
645
|
const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
|
|
646
646
|
throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
|
|
@@ -682,7 +682,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
|
|
|
682
682
|
}
|
|
683
683
|
async function runAppRemove(context, appName, projectRef) {
|
|
684
684
|
ensurePreviewAppMode(context);
|
|
685
|
-
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
|
|
685
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
|
|
686
686
|
const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
|
|
687
687
|
await confirmAppRemoval(context, selectedApp);
|
|
688
688
|
const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
|
|
@@ -703,7 +703,7 @@ async function runAppRemove(context, appName, projectRef) {
|
|
|
703
703
|
nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
|
|
704
704
|
};
|
|
705
705
|
}
|
|
706
|
-
async function resolveAppDomainTarget(context, options) {
|
|
706
|
+
async function resolveAppDomainTarget(context, options, commandName = "app domain") {
|
|
707
707
|
ensurePreviewAppMode(context);
|
|
708
708
|
const branch = resolveDomainBranch(options?.branchName);
|
|
709
709
|
if (toBranchKind(branch.name) !== "production") throw new CliError({
|
|
@@ -717,13 +717,10 @@ async function resolveAppDomainTarget(context, options) {
|
|
|
717
717
|
});
|
|
718
718
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
719
719
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
720
|
-
const
|
|
721
|
-
const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
|
|
722
|
-
if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
|
|
723
|
-
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
720
|
+
const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
|
|
724
721
|
branch,
|
|
725
|
-
|
|
726
|
-
|
|
722
|
+
commandName,
|
|
723
|
+
envProjectId
|
|
727
724
|
});
|
|
728
725
|
const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
729
726
|
explicitAppName: options?.appName,
|
|
@@ -1218,9 +1215,9 @@ async function listApps(context, provider, projectId, branchName) {
|
|
|
1218
1215
|
domain: "project",
|
|
1219
1216
|
summary: "Project not found",
|
|
1220
1217
|
why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
|
|
1221
|
-
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.",
|
|
1222
1219
|
exitCode: 1,
|
|
1223
|
-
nextSteps: ["prisma-cli project show", "prisma-cli
|
|
1220
|
+
nextSteps: ["prisma-cli project show", "prisma-cli project link <id-or-name>"]
|
|
1224
1221
|
});
|
|
1225
1222
|
throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
|
|
1226
1223
|
});
|
|
@@ -1280,8 +1277,9 @@ async function resolveProjectContext(context, client, explicitProject, options)
|
|
|
1280
1277
|
context,
|
|
1281
1278
|
workspace: authState.workspace,
|
|
1282
1279
|
explicitProject,
|
|
1280
|
+
envProjectId: options?.envProjectId,
|
|
1283
1281
|
listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
|
|
1284
|
-
|
|
1282
|
+
commandName: options?.commandName
|
|
1285
1283
|
});
|
|
1286
1284
|
const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
1287
1285
|
return {
|
|
@@ -1862,10 +1860,14 @@ function localResolutionPinStaleError() {
|
|
|
1862
1860
|
domain: "project",
|
|
1863
1861
|
summary: "Local project binding is stale",
|
|
1864
1862
|
why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
1865
|
-
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}
|
|
1863
|
+
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
|
|
1866
1864
|
meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
|
|
1867
1865
|
exitCode: 1,
|
|
1868
|
-
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
|
+
]
|
|
1869
1871
|
});
|
|
1870
1872
|
}
|
|
1871
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
|
{
|