@prisma/cli 3.0.0-dev.38.1 → 3.0.0-dev.39.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 -1
- package/dist/commands/project/index.js +28 -2
- package/dist/controllers/app.js +192 -199
- package/dist/controllers/project.js +47 -2
- package/dist/lib/project/resolution.js +1 -1
- package/dist/lib/project/setup.js +86 -0
- package/dist/presenters/project.js +10 -1
- package/dist/shell/command-meta.js +27 -1
- package/package.json +1 -1
|
@@ -59,7 +59,7 @@ function createRunCommand(runtime) {
|
|
|
59
59
|
}
|
|
60
60
|
function createDeployCommand(runtime) {
|
|
61
61
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("deploy"), runtime), "app.deploy");
|
|
62
|
-
command.addOption(new Option("--app <name>", "App name")).addOption(new Option("--project <id-or-name>", "Project id or name")).addOption(new Option("--branch <name>", "Branch name")).addOption(new Option("--framework <name>", "Framework to deploy").choices([
|
|
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
65
|
"tanstack-start"
|
|
@@ -74,8 +74,10 @@ function createDeployCommand(runtime) {
|
|
|
74
74
|
const httpPort = options.httpPort;
|
|
75
75
|
const envAssignments = options.env;
|
|
76
76
|
const projectRef = options.project;
|
|
77
|
+
const createProjectName = options.createProject;
|
|
77
78
|
await runCommand(runtime, "app.deploy", options, (context) => runAppDeploy(context, appName, {
|
|
78
79
|
projectRef,
|
|
80
|
+
createProjectName,
|
|
79
81
|
branchName,
|
|
80
82
|
entrypoint: entry,
|
|
81
83
|
buildType,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { attachCommandDescriptor } from "../../shell/command-meta.js";
|
|
2
2
|
import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
|
|
3
3
|
import { configureRuntimeCommand } from "../../shell/runtime.js";
|
|
4
|
-
import { runProjectList, runProjectShow } from "../../controllers/project.js";
|
|
4
|
+
import { runProjectCreate, runProjectLink, runProjectList, runProjectShow } from "../../controllers/project.js";
|
|
5
5
|
import { runCommand } from "../../shell/command-runner.js";
|
|
6
|
-
import { renderProjectList, renderProjectShow, serializeProjectList, serializeProjectShow } from "../../presenters/project.js";
|
|
6
|
+
import { renderProjectList, renderProjectSetup, renderProjectShow, serializeProjectList, serializeProjectSetup, serializeProjectShow } from "../../presenters/project.js";
|
|
7
7
|
import { createEnvCommand } from "../env.js";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
//#region src/commands/project/index.ts
|
|
@@ -12,9 +12,35 @@ function createProjectCommand(runtime) {
|
|
|
12
12
|
addCompactGlobalFlags(project);
|
|
13
13
|
project.addCommand(createProjectListCommand(runtime));
|
|
14
14
|
project.addCommand(createProjectShowCommand(runtime));
|
|
15
|
+
project.addCommand(createProjectCreateCommand(runtime));
|
|
16
|
+
project.addCommand(createProjectLinkCommand(runtime));
|
|
15
17
|
project.addCommand(createEnvCommand(runtime));
|
|
16
18
|
return project;
|
|
17
19
|
}
|
|
20
|
+
function createProjectCreateCommand(runtime) {
|
|
21
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "project.create");
|
|
22
|
+
command.argument("<name>", "Project name");
|
|
23
|
+
addGlobalFlags(command);
|
|
24
|
+
command.action(async (name, options) => {
|
|
25
|
+
await runCommand(runtime, "project.create", options, (context) => runProjectCreate(context, String(name)), {
|
|
26
|
+
renderHuman: (context, descriptor, result) => renderProjectSetup(context, descriptor, result),
|
|
27
|
+
renderJson: (result) => serializeProjectSetup(result)
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
return command;
|
|
31
|
+
}
|
|
32
|
+
function createProjectLinkCommand(runtime) {
|
|
33
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("link"), runtime), "project.link");
|
|
34
|
+
command.argument("<id-or-name>", "Project id or name");
|
|
35
|
+
addGlobalFlags(command);
|
|
36
|
+
command.action(async (projectRef, options) => {
|
|
37
|
+
await runCommand(runtime, "project.link", options, (context) => runProjectLink(context, String(projectRef)), {
|
|
38
|
+
renderHuman: (context, descriptor, result) => renderProjectSetup(context, descriptor, result),
|
|
39
|
+
renderJson: (result) => serializeProjectSetup(result)
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
return command;
|
|
43
|
+
}
|
|
18
44
|
function createProjectListCommand(runtime) {
|
|
19
45
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "project.list");
|
|
20
46
|
addGlobalFlags(command);
|
package/dist/controllers/app.js
CHANGED
|
@@ -11,8 +11,9 @@ import { parseEnvAssignments } from "../lib/app/env-vars.js";
|
|
|
11
11
|
import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
|
|
12
12
|
import { 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, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
15
|
-
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
14
|
+
import { inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
15
|
+
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
16
|
+
import { bindProjectToDirectory, formatCommandArgument, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
16
17
|
import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
|
|
17
18
|
import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
|
|
18
19
|
import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
|
|
@@ -96,44 +97,54 @@ async function runAppDeploy(context, appName, options) {
|
|
|
96
97
|
ensurePreviewAppMode(context);
|
|
97
98
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
98
99
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
99
|
-
|
|
100
|
+
assertExclusiveDeployProjectInputs({
|
|
101
|
+
projectRef: options?.projectRef,
|
|
102
|
+
createProjectName: options?.createProjectName,
|
|
103
|
+
envProjectId
|
|
104
|
+
});
|
|
105
|
+
const skipLocalPin = Boolean(envProjectId || options?.projectRef || options?.createProjectName);
|
|
100
106
|
const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
|
|
101
107
|
if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
|
|
102
108
|
const explicitBuildType = Boolean(options?.buildType && options.buildType !== "auto");
|
|
103
|
-
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
104
109
|
if (options?.httpPort) parseDeployHttpPort(options.httpPort);
|
|
105
|
-
|
|
110
|
+
assertSupportedEntrypointForRequestedDeployShape({
|
|
106
111
|
requestedFramework: options?.framework,
|
|
107
112
|
requestedBuildType: options?.buildType,
|
|
108
|
-
explicitBuildType
|
|
113
|
+
explicitBuildType,
|
|
114
|
+
entrypoint: options?.entrypoint
|
|
109
115
|
});
|
|
110
|
-
|
|
111
|
-
assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
|
|
112
|
-
const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
|
|
113
|
-
const firstDeploy = !skipLocalPin && localPin.kind === "missing";
|
|
116
|
+
const branch = await resolveDeployBranch(context, options?.branchName);
|
|
114
117
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
115
|
-
allowCreate: true,
|
|
116
118
|
branch,
|
|
119
|
+
createProjectName: options?.createProjectName,
|
|
117
120
|
envProjectId,
|
|
118
121
|
localPin
|
|
119
122
|
});
|
|
123
|
+
let localPinResult;
|
|
124
|
+
if (target.localPinAction) {
|
|
125
|
+
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
126
|
+
localPinResult = setupResult.localPin;
|
|
127
|
+
maybeRenderProjectLinked(context, setupResult.directory, setupResult.project.name, setupResult.localPin.path);
|
|
128
|
+
}
|
|
129
|
+
let framework = await resolveDeployFramework(context, {
|
|
130
|
+
requestedFramework: options?.framework,
|
|
131
|
+
requestedBuildType: options?.buildType,
|
|
132
|
+
explicitBuildType
|
|
133
|
+
});
|
|
134
|
+
let runtime = resolveDeployRuntime(options?.httpPort, framework);
|
|
135
|
+
assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
|
|
136
|
+
const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
|
|
120
137
|
const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
|
|
121
138
|
explicitAppName: appName,
|
|
122
139
|
explicitAppId: envAppId,
|
|
123
|
-
firstDeploy,
|
|
140
|
+
firstDeploy: Boolean(target.localPinAction),
|
|
124
141
|
inferName: () => inferTargetName(context.runtime.cwd)
|
|
125
142
|
});
|
|
126
143
|
await maybeRenderDeploySetupBlock(context, {
|
|
127
|
-
|
|
128
|
-
workspaceName: target.workspace.name,
|
|
144
|
+
includeDirectory: !target.localPinAction,
|
|
129
145
|
projectName: target.project.name,
|
|
130
|
-
projectAnnotation: annotationForProjectResolution(target.resolution),
|
|
131
146
|
branchName: target.branch.name,
|
|
132
|
-
|
|
133
|
-
appName: selectedApp.displayName,
|
|
134
|
-
appAnnotation: selectedApp.annotation,
|
|
135
|
-
framework,
|
|
136
|
-
runtime
|
|
147
|
+
appName: selectedApp.displayName
|
|
137
148
|
});
|
|
138
149
|
const customized = await maybeCustomizeDeploySettings(context, {
|
|
139
150
|
framework,
|
|
@@ -148,15 +159,6 @@ async function runAppDeploy(context, appName, options) {
|
|
|
148
159
|
const buildType = framework.buildType;
|
|
149
160
|
assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
|
|
150
161
|
const portMapping = parseDeployPortMapping(String(runtime.port));
|
|
151
|
-
const shouldWriteLocalPin = firstDeploy && !skipLocalPin;
|
|
152
|
-
if (shouldWriteLocalPin) {
|
|
153
|
-
await writeLocalResolutionPin(context.runtime.cwd, {
|
|
154
|
-
workspaceId: target.workspace.id,
|
|
155
|
-
projectId: target.project.id
|
|
156
|
-
});
|
|
157
|
-
await ensureLocalResolutionPinGitignore(context.runtime.cwd);
|
|
158
|
-
maybeRenderLocalPinBound(context, target.project.name);
|
|
159
|
-
}
|
|
160
162
|
const progressState = createPreviewDeployProgressState();
|
|
161
163
|
const deployStartedAt = Date.now();
|
|
162
164
|
const deployResult = await provider.deployApp({
|
|
@@ -194,10 +196,7 @@ async function runAppDeploy(context, appName, options) {
|
|
|
194
196
|
},
|
|
195
197
|
deployment: deployResult.deployment,
|
|
196
198
|
durationMs: deployDurationMs,
|
|
197
|
-
localPin:
|
|
198
|
-
path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
199
|
-
written: true
|
|
200
|
-
} : void 0
|
|
199
|
+
localPin: localPinResult
|
|
201
200
|
},
|
|
202
201
|
warnings: [],
|
|
203
202
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
|
|
@@ -719,11 +718,10 @@ async function resolveAppDomainTarget(context, options) {
|
|
|
719
718
|
});
|
|
720
719
|
const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
|
|
721
720
|
const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
|
|
722
|
-
const skipLocalPin = Boolean(envProjectId
|
|
721
|
+
const skipLocalPin = Boolean(envProjectId);
|
|
723
722
|
const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
|
|
724
723
|
if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
|
|
725
724
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
726
|
-
allowCreate: false,
|
|
727
725
|
branch,
|
|
728
726
|
envProjectId,
|
|
729
727
|
localPin
|
|
@@ -1258,7 +1256,7 @@ function createPreviewLogAuthOptions(env) {
|
|
|
1258
1256
|
}
|
|
1259
1257
|
async function requireProviderAndProjectContext(context, explicitProject, options) {
|
|
1260
1258
|
const { client, provider } = await requirePreviewAppProviderWithClient(context);
|
|
1261
|
-
const target = await resolveProjectContext(context, client,
|
|
1259
|
+
const target = await resolveProjectContext(context, client, explicitProject, options);
|
|
1262
1260
|
return {
|
|
1263
1261
|
client,
|
|
1264
1262
|
provider,
|
|
@@ -1276,7 +1274,7 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
|
|
|
1276
1274
|
projectId: target.project.id
|
|
1277
1275
|
};
|
|
1278
1276
|
}
|
|
1279
|
-
async function resolveProjectContext(context, client,
|
|
1277
|
+
async function resolveProjectContext(context, client, explicitProject, options) {
|
|
1280
1278
|
const authState = await requireAuthenticatedAuthState(context);
|
|
1281
1279
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
1282
1280
|
const resolved = await resolveProjectTarget({
|
|
@@ -1284,22 +1282,6 @@ async function resolveProjectContext(context, client, provider, explicitProject,
|
|
|
1284
1282
|
workspace: authState.workspace,
|
|
1285
1283
|
explicitProject,
|
|
1286
1284
|
listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
|
|
1287
|
-
createProject: options?.allowCreate ? async (name) => {
|
|
1288
|
-
const project = await provider.createProject({ name }).catch((error) => {
|
|
1289
|
-
throw createProjectOnFirstDeployError({
|
|
1290
|
-
error,
|
|
1291
|
-
inferredName: name,
|
|
1292
|
-
workspaceName: authState.workspace.name
|
|
1293
|
-
});
|
|
1294
|
-
});
|
|
1295
|
-
return {
|
|
1296
|
-
id: project.id,
|
|
1297
|
-
name: project.name,
|
|
1298
|
-
workspace: authState.workspace
|
|
1299
|
-
};
|
|
1300
|
-
} : void 0,
|
|
1301
|
-
allowCreate: options?.allowCreate,
|
|
1302
|
-
prompt: createSelectPromptPort(context),
|
|
1303
1285
|
remember: true
|
|
1304
1286
|
});
|
|
1305
1287
|
const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
|
|
@@ -1316,30 +1298,30 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1316
1298
|
if (!workspace) throw workspaceRequiredError();
|
|
1317
1299
|
const branch = options.branch ?? await resolveDeployBranch(context, void 0);
|
|
1318
1300
|
const projects = await listRealWorkspaceProjects(client, workspace);
|
|
1319
|
-
|
|
1320
|
-
const project = await provider.createProject({ name }).catch((error) => {
|
|
1321
|
-
throw createProjectOnFirstDeployError({
|
|
1322
|
-
error,
|
|
1323
|
-
inferredName: name,
|
|
1324
|
-
workspaceName: workspace.name
|
|
1325
|
-
});
|
|
1326
|
-
});
|
|
1327
|
-
return {
|
|
1328
|
-
id: project.id,
|
|
1329
|
-
name: project.name,
|
|
1330
|
-
workspace
|
|
1331
|
-
};
|
|
1332
|
-
} : void 0;
|
|
1333
|
-
if (explicitProject) return withDeployBranch(await resolveProjectTarget({
|
|
1334
|
-
context,
|
|
1301
|
+
if (explicitProject) return withDeployBranch({
|
|
1335
1302
|
workspace,
|
|
1336
|
-
explicitProject,
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1303
|
+
project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
|
|
1304
|
+
resolution: {
|
|
1305
|
+
projectSource: "explicit",
|
|
1306
|
+
targetName: explicitProject,
|
|
1307
|
+
targetNameSource: "explicit"
|
|
1308
|
+
},
|
|
1309
|
+
localPinAction: "linked"
|
|
1310
|
+
}, branch);
|
|
1311
|
+
if (options.createProjectName) {
|
|
1312
|
+
const projectName = options.createProjectName.trim();
|
|
1313
|
+
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1314
|
+
return withDeployBranch({
|
|
1315
|
+
workspace,
|
|
1316
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
|
|
1317
|
+
resolution: {
|
|
1318
|
+
projectSource: "created",
|
|
1319
|
+
targetName: projectName,
|
|
1320
|
+
targetNameSource: "explicit"
|
|
1321
|
+
},
|
|
1322
|
+
localPinAction: "created"
|
|
1323
|
+
}, branch);
|
|
1324
|
+
}
|
|
1343
1325
|
if (options.envProjectId) {
|
|
1344
1326
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
1345
1327
|
if (!project) throw projectNotFoundError(options.envProjectId, workspace);
|
|
@@ -1368,15 +1350,91 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1368
1350
|
}
|
|
1369
1351
|
}, branch);
|
|
1370
1352
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1353
|
+
const platformMapping = await resolveDurablePlatformMapping();
|
|
1354
|
+
if (platformMapping && platformMapping.workspace.id === workspace.id) return withDeployBranch({
|
|
1373
1355
|
workspace,
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1356
|
+
project: toProjectSummary(platformMapping),
|
|
1357
|
+
resolution: {
|
|
1358
|
+
projectSource: "platform-mapping",
|
|
1359
|
+
targetName: platformMapping.name,
|
|
1360
|
+
targetNameSource: "platform-mapping"
|
|
1361
|
+
}
|
|
1362
|
+
}, branch);
|
|
1363
|
+
if (canPrompt(context) && !context.flags.yes) return withDeployBranch(await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch);
|
|
1364
|
+
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd));
|
|
1365
|
+
}
|
|
1366
|
+
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1367
|
+
const sortedProjects = sortProjects(projects);
|
|
1368
|
+
const choice = await selectPrompt({
|
|
1369
|
+
input: context.runtime.stdin,
|
|
1370
|
+
output: context.runtime.stderr,
|
|
1371
|
+
message: "Which Project should this directory use?",
|
|
1372
|
+
choices: [
|
|
1373
|
+
...sortedProjects.map((project) => ({
|
|
1374
|
+
label: project.name,
|
|
1375
|
+
value: {
|
|
1376
|
+
kind: "project",
|
|
1377
|
+
project
|
|
1378
|
+
}
|
|
1379
|
+
})),
|
|
1380
|
+
{
|
|
1381
|
+
label: "Create a new Project",
|
|
1382
|
+
value: { kind: "create" }
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
label: "Cancel",
|
|
1386
|
+
value: { kind: "cancel" }
|
|
1387
|
+
}
|
|
1388
|
+
]
|
|
1389
|
+
});
|
|
1390
|
+
if (choice.kind === "cancel") throw usageError("Project setup canceled", "Deploy needs a Project before it can continue.", "Choose an existing Project or create a new one, then rerun deploy.", ["prisma-cli app deploy --project <id-or-name>", "prisma-cli app deploy --create-project <name>"], "project");
|
|
1391
|
+
if (choice.kind === "project") return {
|
|
1392
|
+
workspace,
|
|
1393
|
+
project: toProjectSummary(choice.project),
|
|
1394
|
+
resolution: {
|
|
1395
|
+
projectSource: "prompt",
|
|
1396
|
+
targetName: choice.project.name,
|
|
1397
|
+
targetNameSource: "prompt"
|
|
1398
|
+
},
|
|
1399
|
+
localPinAction: "linked"
|
|
1400
|
+
};
|
|
1401
|
+
const suggestedName = await inferTargetName(context.runtime.cwd);
|
|
1402
|
+
const rawName = await textPrompt({
|
|
1403
|
+
input: context.runtime.stdin,
|
|
1404
|
+
output: context.runtime.stderr,
|
|
1405
|
+
message: "Project name",
|
|
1406
|
+
placeholder: suggestedName.name,
|
|
1407
|
+
validate: (value) => validateProjectSetupNameText(value, suggestedName.name)
|
|
1408
|
+
});
|
|
1409
|
+
const projectName = rawName.trim() || suggestedName.name;
|
|
1410
|
+
return {
|
|
1411
|
+
workspace,
|
|
1412
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
|
|
1413
|
+
resolution: {
|
|
1414
|
+
projectSource: "created",
|
|
1415
|
+
targetName: projectName,
|
|
1416
|
+
targetNameSource: rawName.trim() ? "prompt" : suggestedName.source
|
|
1417
|
+
},
|
|
1418
|
+
localPinAction: "created"
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
async function createProjectForDeploySetup(provider, projectName, workspace) {
|
|
1422
|
+
const created = await provider.createProject({ name: projectName }).catch((error) => {
|
|
1423
|
+
throw projectCreateFailedError(error, projectName, workspace, {
|
|
1424
|
+
nextSteps: [
|
|
1425
|
+
"prisma-cli project list",
|
|
1426
|
+
"prisma-cli app deploy --project <id-or-name>",
|
|
1427
|
+
`prisma-cli app deploy --create-project ${formatCommandArgument(projectName)}`
|
|
1428
|
+
],
|
|
1429
|
+
permissionFix: "Choose an existing Project with --project, or grant the token permission to create Projects in this workspace.",
|
|
1430
|
+
fallbackFix: "Choose an existing Project with --project, or retry after addressing the platform error above."
|
|
1431
|
+
});
|
|
1432
|
+
});
|
|
1433
|
+
return {
|
|
1434
|
+
id: created.id,
|
|
1435
|
+
name: created.name,
|
|
1436
|
+
workspace
|
|
1437
|
+
};
|
|
1380
1438
|
}
|
|
1381
1439
|
function withDeployBranch(target, branch) {
|
|
1382
1440
|
return {
|
|
@@ -1387,15 +1445,26 @@ function withDeployBranch(target, branch) {
|
|
|
1387
1445
|
}
|
|
1388
1446
|
};
|
|
1389
1447
|
}
|
|
1390
|
-
function toProjectSummary(project) {
|
|
1391
|
-
return {
|
|
1392
|
-
id: project.id,
|
|
1393
|
-
name: project.name
|
|
1394
|
-
};
|
|
1395
|
-
}
|
|
1396
1448
|
function toBranchKind(name) {
|
|
1397
1449
|
return name === "production" || name === "main" ? "production" : "preview";
|
|
1398
1450
|
}
|
|
1451
|
+
function assertExclusiveDeployProjectInputs(options) {
|
|
1452
|
+
const provided = [
|
|
1453
|
+
options.projectRef ? "--project" : null,
|
|
1454
|
+
options.createProjectName ? "--create-project" : null,
|
|
1455
|
+
options.envProjectId ? PRISMA_PROJECT_ID_ENV_VAR : null
|
|
1456
|
+
].filter((value) => Boolean(value));
|
|
1457
|
+
if (provided.length <= 1) return;
|
|
1458
|
+
throw usageError("Project selection is ambiguous", `${provided.join(", ")} cannot be used together.`, "Choose exactly one Project source for this deploy.", [
|
|
1459
|
+
"prisma-cli app deploy --project <id-or-name>",
|
|
1460
|
+
"prisma-cli app deploy --create-project <name>",
|
|
1461
|
+
`unset ${PRISMA_PROJECT_ID_ENV_VAR}`
|
|
1462
|
+
], "project");
|
|
1463
|
+
}
|
|
1464
|
+
function validateProjectSetupNameText(value, fallback) {
|
|
1465
|
+
if ((value?.trim() || fallback).trim().length > 0) return;
|
|
1466
|
+
return "Enter a Project name.";
|
|
1467
|
+
}
|
|
1399
1468
|
async function resolveDeployBranch(context, explicitBranchName) {
|
|
1400
1469
|
if (explicitBranchName) return {
|
|
1401
1470
|
name: explicitBranchName,
|
|
@@ -1459,6 +1528,14 @@ function resolveDeployRuntime(requestedHttpPort, framework) {
|
|
|
1459
1528
|
annotation: `${framework.displayName} default`
|
|
1460
1529
|
};
|
|
1461
1530
|
}
|
|
1531
|
+
function assertSupportedEntrypointForRequestedDeployShape(options) {
|
|
1532
|
+
if (options.requestedFramework) {
|
|
1533
|
+
assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
if (!options.explicitBuildType) return;
|
|
1537
|
+
assertSupportedEntrypoint(normalizeBuildType(options.requestedBuildType), options.entrypoint, "deploy");
|
|
1538
|
+
}
|
|
1462
1539
|
async function detectDeployFramework(cwd) {
|
|
1463
1540
|
const packageJson = await readBunPackageJson(cwd);
|
|
1464
1541
|
const nextConfig = await detectNextConfig(cwd);
|
|
@@ -1558,53 +1635,12 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
|
|
|
1558
1635
|
async function maybeRenderDeploySetupBlock(context, details) {
|
|
1559
1636
|
if (context.flags.json || context.flags.quiet) return;
|
|
1560
1637
|
const directory = formatDeployDirectory(context.runtime.cwd);
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
return;
|
|
1564
|
-
}
|
|
1565
|
-
const title = `Setting up your local directory ${formatLocalDirectory(context.runtime.cwd, context.runtime.env)}`;
|
|
1566
|
-
const rows = details.firstDeploy ? [
|
|
1567
|
-
{
|
|
1568
|
-
label: "Workspace",
|
|
1569
|
-
value: details.workspaceName
|
|
1570
|
-
},
|
|
1571
|
-
{
|
|
1572
|
-
label: "Project",
|
|
1573
|
-
value: details.projectName,
|
|
1574
|
-
origin: details.projectAnnotation
|
|
1575
|
-
},
|
|
1576
|
-
{
|
|
1577
|
-
label: "Branch",
|
|
1578
|
-
value: details.branchName,
|
|
1579
|
-
origin: details.branchAnnotation
|
|
1580
|
-
},
|
|
1581
|
-
{
|
|
1582
|
-
label: "App",
|
|
1583
|
-
value: details.appName,
|
|
1584
|
-
origin: details.appAnnotation
|
|
1585
|
-
},
|
|
1586
|
-
{
|
|
1587
|
-
label: "Framework",
|
|
1588
|
-
value: details.framework.displayName,
|
|
1589
|
-
origin: details.framework.annotation
|
|
1590
|
-
},
|
|
1591
|
-
{
|
|
1592
|
-
label: "Runtime",
|
|
1593
|
-
value: `HTTP ${details.runtime.port}`,
|
|
1594
|
-
origin: details.runtime.annotation
|
|
1595
|
-
}
|
|
1596
|
-
] : [];
|
|
1597
|
-
const lines = [
|
|
1598
|
-
title,
|
|
1599
|
-
"",
|
|
1600
|
-
...renderDeployOutputRows(context.ui, rows),
|
|
1601
|
-
""
|
|
1602
|
-
];
|
|
1603
|
-
context.output.stderr.write(`${lines.join("\n")}\n`);
|
|
1638
|
+
const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
|
|
1639
|
+
context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
|
|
1604
1640
|
}
|
|
1605
|
-
function
|
|
1641
|
+
function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
|
|
1606
1642
|
if (context.flags.json || context.flags.quiet) return;
|
|
1607
|
-
context.output.stderr.write(
|
|
1643
|
+
context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
|
|
1608
1644
|
}
|
|
1609
1645
|
async function maybeCustomizeDeploySettings(context, options) {
|
|
1610
1646
|
if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitBuildType || options.explicitHttpPort || !canPrompt(context)) return {
|
|
@@ -1659,19 +1695,6 @@ async function maybeCustomizeDeploySettings(context, options) {
|
|
|
1659
1695
|
runtime
|
|
1660
1696
|
};
|
|
1661
1697
|
}
|
|
1662
|
-
function annotationForProjectResolution(resolution) {
|
|
1663
|
-
switch (resolution.projectSource) {
|
|
1664
|
-
case "explicit": return "set by --project";
|
|
1665
|
-
case "env": return `from ${PRISMA_PROJECT_ID_ENV_VAR}`;
|
|
1666
|
-
case "local-pin": return "from local pin";
|
|
1667
|
-
case "created": return resolution.targetNameSource === "directory-name" ? "created from directory name" : "created from package.json";
|
|
1668
|
-
case "package-name":
|
|
1669
|
-
case "directory-name": return "linked to existing project";
|
|
1670
|
-
case "platform-mapping":
|
|
1671
|
-
case "remembered-local": return "linked to existing project";
|
|
1672
|
-
case "prompt": return "selected by you";
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
1698
|
function frameworkDisplayName(framework) {
|
|
1676
1699
|
switch (framework) {
|
|
1677
1700
|
case "nextjs": return "Next.js";
|
|
@@ -1692,15 +1715,6 @@ function formatDeployDirectory(cwd) {
|
|
|
1692
1715
|
const basename = path.basename(cwd);
|
|
1693
1716
|
return basename ? `./${basename}` : ".";
|
|
1694
1717
|
}
|
|
1695
|
-
function formatLocalDirectory(cwd, env) {
|
|
1696
|
-
const resolved = path.resolve(cwd);
|
|
1697
|
-
const home = env.HOME ? path.resolve(env.HOME) : null;
|
|
1698
|
-
if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
|
|
1699
|
-
const relative = path.relative(home, resolved);
|
|
1700
|
-
return relative ? `~/${relative}` : "~";
|
|
1701
|
-
}
|
|
1702
|
-
return resolved;
|
|
1703
|
-
}
|
|
1704
1718
|
async function readCurrentWorkspaceId(context) {
|
|
1705
1719
|
const state = await context.stateStore.read();
|
|
1706
1720
|
if (state.auth?.workspaceId) return state.auth.workspaceId;
|
|
@@ -1835,52 +1849,31 @@ function readDeployEnvOverride(context, name) {
|
|
|
1835
1849
|
const value = context.runtime.env[name]?.trim();
|
|
1836
1850
|
return value ? value : void 0;
|
|
1837
1851
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
* existing project matches the package.json name (or the cwd basename as a
|
|
1841
|
-
* fallback). When the create call fails the user often doesn't realise the
|
|
1842
|
-
* CLI was attempting to create a project at all — they thought the deploy
|
|
1843
|
-
* would find an existing project. Surface that context, and recommend the
|
|
1844
|
-
* explicit `--project` flag as the unambiguous way out.
|
|
1845
|
-
*/
|
|
1846
|
-
function createProjectOnFirstDeployError(options) {
|
|
1847
|
-
const { error, inferredName, workspaceName } = options;
|
|
1848
|
-
const status = extractHttpStatus(error);
|
|
1849
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1850
|
-
const inferredContext = `No existing project matched the package.json name \`${inferredName}\`, so the CLI attempted to create one.`;
|
|
1851
|
-
const nextSteps = ["prisma-cli project list", "prisma-cli app deploy --project <id-or-name>"];
|
|
1852
|
-
if (status === 401 || status === 403) return new CliError({
|
|
1853
|
-
code: "AUTH_FORBIDDEN",
|
|
1854
|
-
domain: "auth",
|
|
1855
|
-
summary: "Could not create a new project for this deploy",
|
|
1856
|
-
why: `${inferredContext} The platform rejected the create (HTTP ${status}).`,
|
|
1857
|
-
fix: `Pass --project <id-or-name> to deploy into an existing project, or grant the service token project-create permission on workspace \`${workspaceName}\`.`,
|
|
1858
|
-
debug: formatDebugDetails(error),
|
|
1859
|
-
exitCode: 1,
|
|
1860
|
-
nextSteps
|
|
1861
|
-
});
|
|
1852
|
+
function projectSetupRequiredError(projects, suggestedName) {
|
|
1853
|
+
const createCommand = `prisma-cli app deploy --create-project ${formatCommandArgument(suggestedName.name)}`;
|
|
1862
1854
|
return new CliError({
|
|
1863
|
-
code: "
|
|
1864
|
-
domain: "
|
|
1865
|
-
summary: "
|
|
1866
|
-
why:
|
|
1867
|
-
fix: "
|
|
1868
|
-
|
|
1855
|
+
code: "PROJECT_SETUP_REQUIRED",
|
|
1856
|
+
domain: "project",
|
|
1857
|
+
summary: "Choose a Project before deploying this directory",
|
|
1858
|
+
why: "This directory is not linked to a Prisma Project, and deploy will not choose or create one implicitly.",
|
|
1859
|
+
fix: "Choose an existing Project with --project, create one with --create-project, or rerun interactively to pick from the setup list.",
|
|
1860
|
+
meta: {
|
|
1861
|
+
candidates: sortProjects(projects).map((project) => ({
|
|
1862
|
+
id: project.id,
|
|
1863
|
+
name: project.name
|
|
1864
|
+
})),
|
|
1865
|
+
suggestedProjectName: suggestedName.name,
|
|
1866
|
+
suggestedProjectNameSource: suggestedName.source,
|
|
1867
|
+
recoveryCommands: ["prisma-cli app deploy --project <id-or-name>", createCommand]
|
|
1868
|
+
},
|
|
1869
1869
|
exitCode: 1,
|
|
1870
|
-
nextSteps
|
|
1870
|
+
nextSteps: [
|
|
1871
|
+
"prisma-cli project list",
|
|
1872
|
+
"prisma-cli app deploy --project <id-or-name>",
|
|
1873
|
+
createCommand
|
|
1874
|
+
]
|
|
1871
1875
|
});
|
|
1872
1876
|
}
|
|
1873
|
-
function extractHttpStatus(error) {
|
|
1874
|
-
if (!error || typeof error !== "object") return null;
|
|
1875
|
-
const candidate = error;
|
|
1876
|
-
if (typeof candidate.statusCode === "number") return candidate.statusCode;
|
|
1877
|
-
if (typeof candidate.status === "number") return candidate.status;
|
|
1878
|
-
if (typeof candidate.message === "string") {
|
|
1879
|
-
const match = /\(HTTP (\d{3})\)/.exec(candidate.message);
|
|
1880
|
-
if (match) return Number.parseInt(match[1], 10);
|
|
1881
|
-
}
|
|
1882
|
-
return null;
|
|
1883
|
-
}
|
|
1884
1877
|
function noDeploymentsError(summary, why) {
|
|
1885
1878
|
return new CliError({
|
|
1886
1879
|
code: "NO_DEPLOYMENTS",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
1
|
+
import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
2
|
import { renderSummaryLine } from "../shell/ui.js";
|
|
3
3
|
import { canPrompt } from "../shell/runtime.js";
|
|
4
4
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
5
5
|
import { resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
6
|
+
import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup } from "../lib/project/setup.js";
|
|
7
|
+
import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
6
8
|
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
|
|
7
9
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
8
10
|
import { parseGitHubRepositoryUrl, readGitOriginRemote } from "../adapters/git.js";
|
|
@@ -48,6 +50,44 @@ async function runProjectShow(context, explicitProject) {
|
|
|
48
50
|
nextSteps: []
|
|
49
51
|
};
|
|
50
52
|
}
|
|
53
|
+
async function runProjectCreate(context, projectName) {
|
|
54
|
+
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
55
|
+
if (!workspace) throw workspaceRequiredError();
|
|
56
|
+
if (!isValidProjectSetupName(projectName)) throw projectSetupNameRequiredError("project create");
|
|
57
|
+
if (!isRealMode(context)) throw featureUnavailableError("Project create is not available in fixture mode", "Creating Projects requires live platform integration.", "Rerun without fixture mode enabled to create a Project.", ["prisma-cli auth login"], "project");
|
|
58
|
+
const client = await requireComputeAuth(context.runtime.env);
|
|
59
|
+
if (!client) throw authRequiredError();
|
|
60
|
+
const provider = createPreviewAppProvider(client);
|
|
61
|
+
const name = projectName.trim();
|
|
62
|
+
const created = await provider.createProject({ name }).catch((error) => {
|
|
63
|
+
throw projectCreateFailedError(error, name, workspace, {
|
|
64
|
+
nextSteps: ["prisma-cli project list", "prisma-cli project link <id-or-name>"],
|
|
65
|
+
permissionFix: "Grant the token permission to create Projects in this workspace, or link an existing Project.",
|
|
66
|
+
fallbackFix: "Retry the command, or choose an existing Project with prisma-cli project link <id-or-name>."
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
command: "project.create",
|
|
71
|
+
result: await bindProjectToDirectory(context, workspace, {
|
|
72
|
+
id: created.id,
|
|
73
|
+
name: created.name
|
|
74
|
+
}, "created"),
|
|
75
|
+
warnings: [],
|
|
76
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function runProjectLink(context, projectRef) {
|
|
80
|
+
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
81
|
+
if (!workspace) throw workspaceRequiredError();
|
|
82
|
+
if (!projectRef || !projectRef.trim()) throw usageError("Project link requires a Project id or name", "The command cannot choose a Project without an explicit id or name.", "Pass the Project id or name as the first argument.", ["prisma-cli project link proj_123"], "project");
|
|
83
|
+
const projects = isRealMode(context) ? await listRealProjectsForLink(context, workspace) : listFixtureWorkspaceProjects(context, workspace);
|
|
84
|
+
return {
|
|
85
|
+
command: "project.link",
|
|
86
|
+
result: await bindProjectToDirectory(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked"),
|
|
87
|
+
warnings: [],
|
|
88
|
+
nextSteps: ["prisma-cli app deploy"]
|
|
89
|
+
};
|
|
90
|
+
}
|
|
51
91
|
async function runGitConnect(context, gitUrl, options = {}) {
|
|
52
92
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
53
93
|
if (!workspace) throw workspaceRequiredError();
|
|
@@ -163,6 +203,11 @@ async function resolveProjectShowInRealMode(context, workspace, explicitProject)
|
|
|
163
203
|
remember: false
|
|
164
204
|
});
|
|
165
205
|
}
|
|
206
|
+
async function listRealProjectsForLink(context, workspace) {
|
|
207
|
+
const client = await requireComputeAuth(context.runtime.env);
|
|
208
|
+
if (!client) throw authRequiredError();
|
|
209
|
+
return listRealWorkspaceProjects(client, workspace);
|
|
210
|
+
}
|
|
166
211
|
async function resolveProjectShowInFixtureMode(context, workspace, explicitProject) {
|
|
167
212
|
return resolveProjectTarget({
|
|
168
213
|
context,
|
|
@@ -501,4 +546,4 @@ function toProjectSummary(project) {
|
|
|
501
546
|
};
|
|
502
547
|
}
|
|
503
548
|
//#endregion
|
|
504
|
-
export { listRealWorkspaceProjects, runGitConnect, runGitDisconnect, runProjectList, runProjectShow };
|
|
549
|
+
export { listRealWorkspaceProjects, runGitConnect, runGitDisconnect, runProjectCreate, runProjectLink, runProjectList, runProjectShow };
|
|
@@ -198,4 +198,4 @@ function toProjectSummary(project) {
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
//#endregion
|
|
201
|
-
export { inferTargetName, projectNotFoundError, resolveProjectTarget, sortProjects };
|
|
201
|
+
export { inferTargetName, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { CliError, usageError } from "../../shell/errors.js";
|
|
2
|
+
import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
|
|
3
|
+
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
|
|
4
|
+
//#region src/lib/project/setup.ts
|
|
5
|
+
function isValidProjectSetupName(projectName) {
|
|
6
|
+
return projectName.trim().length > 0;
|
|
7
|
+
}
|
|
8
|
+
function resolveProjectForSetup(projectRef, projects, workspace) {
|
|
9
|
+
const matches = projects.filter((project) => project.id === projectRef || project.name === projectRef);
|
|
10
|
+
if (matches.length === 1) return matches[0];
|
|
11
|
+
if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
|
|
12
|
+
throw projectNotFoundError(projectRef, workspace);
|
|
13
|
+
}
|
|
14
|
+
async function bindProjectToDirectory(context, workspace, project, action) {
|
|
15
|
+
await writeLocalResolutionPin(context.runtime.cwd, {
|
|
16
|
+
workspaceId: workspace.id,
|
|
17
|
+
projectId: project.id
|
|
18
|
+
});
|
|
19
|
+
await ensureLocalResolutionPinGitignore(context.runtime.cwd);
|
|
20
|
+
return {
|
|
21
|
+
workspace,
|
|
22
|
+
project,
|
|
23
|
+
directory: formatSetupDirectory(context.runtime.cwd),
|
|
24
|
+
localPin: {
|
|
25
|
+
path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
26
|
+
written: true
|
|
27
|
+
},
|
|
28
|
+
action
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function toProjectSummary(project) {
|
|
32
|
+
return {
|
|
33
|
+
id: project.id,
|
|
34
|
+
name: project.name
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function projectSetupNameRequiredError(command) {
|
|
38
|
+
return usageError("Project create requires a name", "The project name must be a non-empty value.", "Pass a Project name explicitly.", [`prisma-cli ${command} my-app`], "project");
|
|
39
|
+
}
|
|
40
|
+
function projectCreateFailedError(error, projectName, workspace, options) {
|
|
41
|
+
const status = extractHttpStatus(error);
|
|
42
|
+
if (status === 401 || status === 403) return new CliError({
|
|
43
|
+
code: "PROJECT_CREATE_FAILED",
|
|
44
|
+
domain: "project",
|
|
45
|
+
summary: `Could not create Project "${projectName}"`,
|
|
46
|
+
why: `The platform rejected the Project create in workspace "${workspace.name}" (HTTP ${status}).`,
|
|
47
|
+
fix: options.permissionFix,
|
|
48
|
+
debug: formatDebugDetails(error),
|
|
49
|
+
exitCode: 1,
|
|
50
|
+
nextSteps: options.nextSteps
|
|
51
|
+
});
|
|
52
|
+
return new CliError({
|
|
53
|
+
code: "PROJECT_CREATE_FAILED",
|
|
54
|
+
domain: "project",
|
|
55
|
+
summary: `Could not create Project "${projectName}"`,
|
|
56
|
+
why: error instanceof Error ? error.message : String(error),
|
|
57
|
+
fix: options.fallbackFix,
|
|
58
|
+
debug: formatDebugDetails(error),
|
|
59
|
+
exitCode: 1,
|
|
60
|
+
nextSteps: options.nextSteps
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function formatCommandArgument(value) {
|
|
64
|
+
return /^[A-Za-z0-9._/-]+$/.test(value) ? value : JSON.stringify(value);
|
|
65
|
+
}
|
|
66
|
+
function formatSetupDirectory(cwd) {
|
|
67
|
+
const basename = cwd.split(/[\\/]/).filter(Boolean).pop();
|
|
68
|
+
return basename ? `./${basename}` : ".";
|
|
69
|
+
}
|
|
70
|
+
function extractHttpStatus(error) {
|
|
71
|
+
if (!error || typeof error !== "object") return null;
|
|
72
|
+
const candidate = error;
|
|
73
|
+
if (typeof candidate.statusCode === "number") return candidate.statusCode;
|
|
74
|
+
if (typeof candidate.status === "number") return candidate.status;
|
|
75
|
+
if (typeof candidate.message === "string") {
|
|
76
|
+
const match = /\(HTTP (\d{3})\)/.exec(candidate.message);
|
|
77
|
+
if (match) return Number.parseInt(match[1], 10);
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
function formatDebugDetails(error) {
|
|
82
|
+
if (error instanceof Error) return error.stack ?? error.message;
|
|
83
|
+
return typeof error === "string" ? error : null;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { bindProjectToDirectory, formatCommandArgument, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderSummaryLine } from "../shell/ui.js";
|
|
1
2
|
import { renderList, renderMutate, renderShow, serializeList } from "../output/patterns.js";
|
|
2
3
|
//#region src/presenters/project.ts
|
|
3
4
|
function renderProjectList(context, descriptor, result) {
|
|
@@ -51,6 +52,14 @@ function renderProjectShow(context, descriptor, result) {
|
|
|
51
52
|
function serializeProjectShow(result) {
|
|
52
53
|
return result;
|
|
53
54
|
}
|
|
55
|
+
function renderProjectSetup(context, _descriptor, result) {
|
|
56
|
+
const lines = result.action === "created" ? [renderSummaryLine(context.ui, "success", `Created Project "${result.project.name}"`)] : [];
|
|
57
|
+
lines.push(renderSummaryLine(context.ui, "success", `Linked "${result.directory}" to Project "${result.project.name}"`), `Saved ${result.localPin.path}`);
|
|
58
|
+
return lines;
|
|
59
|
+
}
|
|
60
|
+
function serializeProjectSetup(result) {
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
54
63
|
function renderGitConnect(context, descriptor, result) {
|
|
55
64
|
const connection = result.repositoryConnection;
|
|
56
65
|
return renderMutate({
|
|
@@ -124,4 +133,4 @@ function formatGitConnectionDetail(status) {
|
|
|
124
133
|
}
|
|
125
134
|
}
|
|
126
135
|
//#endregion
|
|
127
|
-
export { renderGitConnect, renderGitDisconnect, renderProjectList, renderProjectShow, serializeProjectList, serializeProjectShow };
|
|
136
|
+
export { renderGitConnect, renderGitDisconnect, renderProjectList, renderProjectSetup, renderProjectShow, serializeProjectList, serializeProjectSetup, serializeProjectShow };
|
|
@@ -54,7 +54,11 @@ const DESCRIPTORS = [
|
|
|
54
54
|
id: "project",
|
|
55
55
|
path: ["prisma", "project"],
|
|
56
56
|
description: "Manage and inspect your Prisma projects",
|
|
57
|
-
examples: [
|
|
57
|
+
examples: [
|
|
58
|
+
"prisma-cli project list",
|
|
59
|
+
"prisma-cli project link proj_123",
|
|
60
|
+
"prisma-cli project create my-app"
|
|
61
|
+
]
|
|
58
62
|
},
|
|
59
63
|
{
|
|
60
64
|
id: "app",
|
|
@@ -94,6 +98,26 @@ const DESCRIPTORS = [
|
|
|
94
98
|
description: "Show which project is active for this directory",
|
|
95
99
|
examples: ["prisma-cli project show", "prisma-cli project show --project proj_123 --json"]
|
|
96
100
|
},
|
|
101
|
+
{
|
|
102
|
+
id: "project.create",
|
|
103
|
+
path: [
|
|
104
|
+
"prisma",
|
|
105
|
+
"project",
|
|
106
|
+
"create"
|
|
107
|
+
],
|
|
108
|
+
description: "Create a Project and link this directory",
|
|
109
|
+
examples: ["prisma-cli project create my-app", "prisma-cli project create my-app --json"]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "project.link",
|
|
113
|
+
path: [
|
|
114
|
+
"prisma",
|
|
115
|
+
"project",
|
|
116
|
+
"link"
|
|
117
|
+
],
|
|
118
|
+
description: "Link this directory to an existing Project",
|
|
119
|
+
examples: ["prisma-cli project link proj_123", "prisma-cli project link \"Acme Dashboard\" --json"]
|
|
120
|
+
},
|
|
97
121
|
{
|
|
98
122
|
id: "git.connect",
|
|
99
123
|
path: [
|
|
@@ -183,6 +207,8 @@ const DESCRIPTORS = [
|
|
|
183
207
|
longDescription: "Agent skills for guided Next.js deploys are available from the Prisma CLI skill cluster.",
|
|
184
208
|
examples: [
|
|
185
209
|
"prisma-cli app deploy",
|
|
210
|
+
"prisma-cli app deploy --project proj_123",
|
|
211
|
+
"prisma-cli app deploy --create-project my-app --yes",
|
|
186
212
|
"prisma-cli app deploy --app my-app --env DATABASE_URL=postgresql://example",
|
|
187
213
|
"prisma-cli app deploy --app my-app --framework nextjs --http-port 3000",
|
|
188
214
|
"prisma-cli app deploy --branch feat-login --framework hono",
|