@prisma/cli 3.0.0-beta.27 → 3.0.0-beta.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/adapters/mock-api.js +73 -0
- package/dist/cli2.js +2 -0
- package/dist/commands/app/index.js +3 -2
- package/dist/commands/bucket/index.js +121 -0
- package/dist/commands/project/index.js +3 -2
- package/dist/controllers/app.js +29 -10
- package/dist/controllers/bucket.js +265 -0
- package/dist/controllers/project.js +5 -2
- package/dist/lib/app/app-provider.js +3 -1
- package/dist/lib/bucket/provider.js +139 -0
- package/dist/lib/project/resolution.js +2 -1
- package/dist/lib/project/setup.js +2 -1
- package/dist/presenters/app.js +5 -0
- package/dist/presenters/bucket.js +174 -0
- package/dist/presenters/project.js +7 -2
- package/dist/shell/command-meta.js +105 -2
- package/dist/use-cases/project.js +2 -1
- package/package.json +2 -2
- package/dist/lib/app/app-interaction.js +0 -5
package/README.md
CHANGED
|
@@ -76,6 +76,8 @@ The beta package exposes `prisma-cli` so it can coexist with the existing
|
|
|
76
76
|
| `project` | List projects, show the resolved project, and manage project environment variables. |
|
|
77
77
|
| `git` | Connect or disconnect a project from a GitHub repository. |
|
|
78
78
|
| `branch` | List Prisma branches for the resolved project. |
|
|
79
|
+
| `database` | Create, inspect, and remove Prisma Postgres databases and their connection strings. |
|
|
80
|
+
| `bucket` | Create, list, and delete Tigris object-store buckets and their access keys. |
|
|
79
81
|
| `app` | Build, run, deploy, inspect, open, stream logs, promote, roll back, and remove apps. |
|
|
80
82
|
|
|
81
83
|
Common examples:
|
|
@@ -227,6 +227,79 @@ var MockApi = class MockApi {
|
|
|
227
227
|
connectionString
|
|
228
228
|
};
|
|
229
229
|
}
|
|
230
|
+
listBucketsForProject(projectId, branchName) {
|
|
231
|
+
return (this.data.buckets ?? []).filter((bucket) => bucket.projectId === projectId && (!branchName || this.data.branches.find((branch) => branch.id === bucket.branchId && branch.name === branchName)));
|
|
232
|
+
}
|
|
233
|
+
getBucket(bucketId) {
|
|
234
|
+
return (this.data.buckets ?? []).find((bucket) => bucket.id === bucketId);
|
|
235
|
+
}
|
|
236
|
+
createBucket(input) {
|
|
237
|
+
this.data.buckets ??= [];
|
|
238
|
+
let branchId = null;
|
|
239
|
+
if (input.branchGitName) {
|
|
240
|
+
const branch = this.data.branches.find((b) => b.projectId === input.projectId && b.name === input.branchGitName);
|
|
241
|
+
if (!branch) return;
|
|
242
|
+
branchId = branch.id;
|
|
243
|
+
}
|
|
244
|
+
const bucket = {
|
|
245
|
+
id: `bkt_${this.data.buckets.length + 1e3}`,
|
|
246
|
+
projectId: input.projectId,
|
|
247
|
+
branchId,
|
|
248
|
+
name: input.name ?? `bucket-${this.data.buckets.length + 1e3}`,
|
|
249
|
+
status: "ready",
|
|
250
|
+
createdAt: "2026-06-09T00:00:00.000Z"
|
|
251
|
+
};
|
|
252
|
+
this.data.buckets.push(bucket);
|
|
253
|
+
return bucket;
|
|
254
|
+
}
|
|
255
|
+
deleteBucket(bucketId) {
|
|
256
|
+
this.data.buckets ??= [];
|
|
257
|
+
this.data.bucketKeys ??= [];
|
|
258
|
+
const bucket = this.getBucket(bucketId);
|
|
259
|
+
if (!bucket) return;
|
|
260
|
+
this.data.buckets = this.data.buckets.filter((candidate) => candidate.id !== bucketId);
|
|
261
|
+
this.data.bucketKeys = this.data.bucketKeys.filter((key) => key.bucketId !== bucketId);
|
|
262
|
+
return bucket;
|
|
263
|
+
}
|
|
264
|
+
listBucketKeys(bucketId) {
|
|
265
|
+
return (this.data.bucketKeys ?? []).filter((key) => key.bucketId === bucketId);
|
|
266
|
+
}
|
|
267
|
+
createBucketKey(input) {
|
|
268
|
+
const bucket = this.getBucket(input.bucketId);
|
|
269
|
+
if (!bucket) return;
|
|
270
|
+
this.data.bucketKeys ??= [];
|
|
271
|
+
const secretAccessKey = `secret-${input.bucketId}-${this.data.bucketKeys.length + 1}`;
|
|
272
|
+
const accessKeyId = `AKIA${input.bucketId.toUpperCase().replace(/_/g, "")}${this.data.bucketKeys.length + 1}`;
|
|
273
|
+
const endpoint = `https://fly.storage.tigris.dev`;
|
|
274
|
+
const bucketName = bucket.name;
|
|
275
|
+
const key = {
|
|
276
|
+
id: `bkey_${this.data.bucketKeys.length + 1e3}`,
|
|
277
|
+
bucketId: input.bucketId,
|
|
278
|
+
name: input.name ?? `key-${this.data.bucketKeys.length + 1e3}`,
|
|
279
|
+
role: input.role,
|
|
280
|
+
valueHint: `${accessKeyId.slice(0, 8)}...`,
|
|
281
|
+
createdAt: "2026-06-09T00:00:00.000Z",
|
|
282
|
+
secretAccessKey,
|
|
283
|
+
accessKeyId,
|
|
284
|
+
endpoint,
|
|
285
|
+
bucketName
|
|
286
|
+
};
|
|
287
|
+
this.data.bucketKeys.push(key);
|
|
288
|
+
return {
|
|
289
|
+
key,
|
|
290
|
+
secretAccessKey,
|
|
291
|
+
accessKeyId,
|
|
292
|
+
endpoint,
|
|
293
|
+
bucketName
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
deleteBucketKey(bucketId, keyId) {
|
|
297
|
+
this.data.bucketKeys ??= [];
|
|
298
|
+
const key = (this.data.bucketKeys ?? []).find((candidate) => candidate.id === keyId && candidate.bucketId === bucketId);
|
|
299
|
+
if (!key) return;
|
|
300
|
+
this.data.bucketKeys = this.data.bucketKeys.filter((candidate) => candidate.id !== keyId);
|
|
301
|
+
return key;
|
|
302
|
+
}
|
|
230
303
|
};
|
|
231
304
|
//#endregion
|
|
232
305
|
export { MockApi };
|
package/dist/cli2.js
CHANGED
|
@@ -9,6 +9,7 @@ import "./shell/prompt.js";
|
|
|
9
9
|
import { createAppCommand } from "./commands/app/index.js";
|
|
10
10
|
import { createAuthCommand } from "./commands/auth/index.js";
|
|
11
11
|
import { createBranchCommand } from "./commands/branch/index.js";
|
|
12
|
+
import { createBucketCommand } from "./commands/bucket/index.js";
|
|
12
13
|
import { createBuildCommand } from "./commands/build/index.js";
|
|
13
14
|
import { createDatabaseCommand } from "./commands/database/index.js";
|
|
14
15
|
import { getCliName, getCliVersion } from "./lib/version.js";
|
|
@@ -67,6 +68,7 @@ function createProgram(runtime) {
|
|
|
67
68
|
program.addCommand(createBranchCommand(runtime));
|
|
68
69
|
program.addCommand(createBuildCommand(runtime));
|
|
69
70
|
program.addCommand(createDatabaseCommand(runtime));
|
|
71
|
+
program.addCommand(createBucketCommand(runtime));
|
|
70
72
|
program.addCommand(createAppCommand(runtime));
|
|
71
73
|
return program;
|
|
72
74
|
}
|
|
@@ -333,12 +333,13 @@ function createRollbackCommand(runtime) {
|
|
|
333
333
|
}
|
|
334
334
|
function createRemoveCommand(runtime) {
|
|
335
335
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("remove"), runtime), "app.remove");
|
|
336
|
-
command.argument("[app]", "App target from prisma.compute.ts when the config defines multiple apps").addOption(new Option("--app <name>", "App name")).addOption(new Option("--project <id-or-name>", "Project id or name"));
|
|
336
|
+
command.argument("[app]", "App target from prisma.compute.ts when the config defines multiple apps").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"));
|
|
337
337
|
addGlobalFlags(command);
|
|
338
338
|
command.action(async (configTarget, options) => {
|
|
339
339
|
const appName = options.app;
|
|
340
340
|
const projectRef = options.project;
|
|
341
|
-
|
|
341
|
+
const branchName = options.branch;
|
|
342
|
+
await runCommand(runtime, "app.remove", options, (context) => runAppRemove(context, appName, projectRef, configTarget, branchName), {
|
|
342
343
|
renderHuman: (context, descriptor, result) => renderAppRemove(context, descriptor, result),
|
|
343
344
|
renderJson: (result) => serializeAppRemove(result)
|
|
344
345
|
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { attachCommandDescriptor } from "../../shell/command-meta.js";
|
|
2
|
+
import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
|
|
3
|
+
import { configureRuntimeCommand } from "../../shell/runtime.js";
|
|
4
|
+
import { runCommand } from "../../shell/command-runner.js";
|
|
5
|
+
import { runBucketCreate, runBucketDelete, runBucketKeyCreate, runBucketKeyDelete, runBucketKeyList, runBucketList } from "../../controllers/bucket.js";
|
|
6
|
+
import { renderBucketCreate, renderBucketDelete, renderBucketKeyCreate, renderBucketKeyCreateStdout, renderBucketKeyDelete, renderBucketKeyList, renderBucketList, serializeBucketCreate, serializeBucketDelete, serializeBucketKeyCreate, serializeBucketKeyDelete, serializeBucketKeyList, serializeBucketList } from "../../presenters/bucket.js";
|
|
7
|
+
import { Command, Option } from "commander";
|
|
8
|
+
//#region src/commands/bucket/index.ts
|
|
9
|
+
function createBucketCommand(runtime) {
|
|
10
|
+
const bucket = attachCommandDescriptor(configureRuntimeCommand(new Command("bucket"), runtime), "bucket");
|
|
11
|
+
addCompactGlobalFlags(bucket);
|
|
12
|
+
bucket.addCommand(createBucketListCommand(runtime));
|
|
13
|
+
bucket.addCommand(createBucketCreateCommand(runtime));
|
|
14
|
+
bucket.addCommand(createBucketDeleteCommand(runtime));
|
|
15
|
+
bucket.addCommand(createBucketKeyCommand(runtime));
|
|
16
|
+
return bucket;
|
|
17
|
+
}
|
|
18
|
+
function addProjectAndBranchOptions(command) {
|
|
19
|
+
return command.addOption(new Option("--project <id-or-name>", "Project id or name")).addOption(new Option("--branch <git-name>", "Branch git name"));
|
|
20
|
+
}
|
|
21
|
+
function createBucketListCommand(runtime) {
|
|
22
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "bucket.list");
|
|
23
|
+
addProjectAndBranchOptions(command);
|
|
24
|
+
addGlobalFlags(command);
|
|
25
|
+
command.action(async (options) => {
|
|
26
|
+
const projectRef = options.project;
|
|
27
|
+
const branchName = options.branch;
|
|
28
|
+
await runCommand(runtime, "bucket.list", options, (context) => runBucketList(context, {
|
|
29
|
+
projectRef,
|
|
30
|
+
branchName
|
|
31
|
+
}), {
|
|
32
|
+
renderHuman: (context, descriptor, result) => renderBucketList(context, descriptor, result),
|
|
33
|
+
renderJson: (result) => serializeBucketList(result)
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return command;
|
|
37
|
+
}
|
|
38
|
+
function createBucketCreateCommand(runtime) {
|
|
39
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "bucket.create");
|
|
40
|
+
command.addOption(new Option("--name <name>", "Bucket display name (auto-generated if omitted)"));
|
|
41
|
+
addProjectAndBranchOptions(command);
|
|
42
|
+
addGlobalFlags(command);
|
|
43
|
+
command.action(async (options) => {
|
|
44
|
+
const projectRef = options.project;
|
|
45
|
+
const branchName = options.branch;
|
|
46
|
+
const name = options.name;
|
|
47
|
+
await runCommand(runtime, "bucket.create", options, (context) => runBucketCreate(context, {
|
|
48
|
+
projectRef,
|
|
49
|
+
branchName,
|
|
50
|
+
name
|
|
51
|
+
}), {
|
|
52
|
+
renderHuman: (context, descriptor, result) => renderBucketCreate(context, descriptor, result),
|
|
53
|
+
renderJson: (result) => serializeBucketCreate(result)
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
return command;
|
|
57
|
+
}
|
|
58
|
+
function createBucketDeleteCommand(runtime) {
|
|
59
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("delete"), runtime), "bucket.delete");
|
|
60
|
+
command.argument("<bucketId>", "Bucket id");
|
|
61
|
+
addGlobalFlags(command);
|
|
62
|
+
command.action(async (bucketId, options) => {
|
|
63
|
+
await runCommand(runtime, "bucket.delete", options, (context) => runBucketDelete(context, bucketId), {
|
|
64
|
+
renderHuman: (context, descriptor, result) => renderBucketDelete(context, descriptor, result),
|
|
65
|
+
renderJson: (result) => serializeBucketDelete(result)
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
return command;
|
|
69
|
+
}
|
|
70
|
+
function createBucketKeyCommand(runtime) {
|
|
71
|
+
const key = attachCommandDescriptor(configureRuntimeCommand(new Command("key"), runtime), "bucket.key");
|
|
72
|
+
addCompactGlobalFlags(key);
|
|
73
|
+
key.addCommand(createBucketKeyListCommand(runtime));
|
|
74
|
+
key.addCommand(createBucketKeyCreateCommand(runtime));
|
|
75
|
+
key.addCommand(createBucketKeyDeleteCommand(runtime));
|
|
76
|
+
return key;
|
|
77
|
+
}
|
|
78
|
+
function createBucketKeyListCommand(runtime) {
|
|
79
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "bucket.key.list");
|
|
80
|
+
command.argument("<bucketId>", "Bucket id");
|
|
81
|
+
addGlobalFlags(command);
|
|
82
|
+
command.action(async (bucketId, options) => {
|
|
83
|
+
await runCommand(runtime, "bucket.key.list", options, (context) => runBucketKeyList(context, bucketId), {
|
|
84
|
+
renderHuman: (context, descriptor, result) => renderBucketKeyList(context, descriptor, result),
|
|
85
|
+
renderJson: (result) => serializeBucketKeyList(result)
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
return command;
|
|
89
|
+
}
|
|
90
|
+
function createBucketKeyCreateCommand(runtime) {
|
|
91
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "bucket.key.create");
|
|
92
|
+
command.argument("<bucketId>", "Bucket id").addOption(new Option("--role <role>", "Access role (default: read_write)").choices(["read", "read_write"])).addOption(new Option("--name <name>", "Key display name (auto-generated if omitted)"));
|
|
93
|
+
addGlobalFlags(command);
|
|
94
|
+
command.action(async (bucketId, options) => {
|
|
95
|
+
const role = options.role;
|
|
96
|
+
const name = options.name;
|
|
97
|
+
await runCommand(runtime, "bucket.key.create", options, (context) => runBucketKeyCreate(context, bucketId, {
|
|
98
|
+
role,
|
|
99
|
+
name
|
|
100
|
+
}), {
|
|
101
|
+
renderStdout: (context, descriptor, result) => renderBucketKeyCreateStdout(context, descriptor, result),
|
|
102
|
+
renderHuman: (context, descriptor, result) => renderBucketKeyCreate(context, descriptor, result),
|
|
103
|
+
renderJson: (result) => serializeBucketKeyCreate(result)
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return command;
|
|
107
|
+
}
|
|
108
|
+
function createBucketKeyDeleteCommand(runtime) {
|
|
109
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("delete"), runtime), "bucket.key.delete");
|
|
110
|
+
command.argument("<bucketId>", "Bucket id").argument("<keyId>", "Key id");
|
|
111
|
+
addGlobalFlags(command);
|
|
112
|
+
command.action(async (bucketId, keyId, options) => {
|
|
113
|
+
await runCommand(runtime, "bucket.key.delete", options, (context) => runBucketKeyDelete(context, bucketId, keyId), {
|
|
114
|
+
renderHuman: (context, descriptor, result) => renderBucketKeyDelete(context, descriptor, result),
|
|
115
|
+
renderJson: (result) => serializeBucketKeyDelete(result)
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
return command;
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { createBucketCommand };
|
|
@@ -67,10 +67,11 @@ function createProjectTransferCommand(runtime) {
|
|
|
67
67
|
}
|
|
68
68
|
function createProjectCreateCommand(runtime) {
|
|
69
69
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "project.create");
|
|
70
|
-
command.argument("<name>", "Project name");
|
|
70
|
+
command.argument("<name>", "Project name").addOption(new Option("--region <region>", "Prisma Compute region id"));
|
|
71
71
|
addGlobalFlags(command);
|
|
72
72
|
command.action(async (name, options) => {
|
|
73
|
-
|
|
73
|
+
const region = options.region;
|
|
74
|
+
await runCommand(runtime, "project.create", options, (context) => runProjectCreate(context, String(name), { region }), {
|
|
74
75
|
renderHuman: (context, descriptor, result) => renderProjectSetup(context, descriptor, result),
|
|
75
76
|
renderJson: (result) => serializeProjectSetup(result)
|
|
76
77
|
});
|
package/dist/controllers/app.js
CHANGED
|
@@ -5,12 +5,11 @@ import { canPrompt } from "../shell/runtime.js";
|
|
|
5
5
|
import { writeJsonEvent } from "../shell/output.js";
|
|
6
6
|
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
7
7
|
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
8
|
-
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
9
|
-
import "../lib/app/app-interaction.js";
|
|
10
8
|
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
|
|
11
9
|
import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
|
|
12
10
|
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
13
11
|
import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
|
|
12
|
+
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
14
13
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
15
14
|
import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
16
15
|
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
@@ -281,6 +280,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
|
281
280
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
282
281
|
branch,
|
|
283
282
|
createProjectName: options?.createProjectName,
|
|
283
|
+
createProjectRegion: deployRegion?.value,
|
|
284
284
|
envProjectId,
|
|
285
285
|
localPin
|
|
286
286
|
});
|
|
@@ -416,6 +416,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
|
416
416
|
entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
|
|
417
417
|
httpPort: runtime.port,
|
|
418
418
|
region: deployResult.app.region ?? selectedApp.region ?? null,
|
|
419
|
+
regionSource: deployRegion?.annotation ?? null,
|
|
419
420
|
envVars: envVarNames(envVars)
|
|
420
421
|
},
|
|
421
422
|
durationMs: deployDurationMs,
|
|
@@ -966,11 +967,19 @@ async function runAppRollback(context, appName, deploymentId, projectRef, config
|
|
|
966
967
|
nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
|
|
967
968
|
};
|
|
968
969
|
}
|
|
969
|
-
|
|
970
|
+
/**
|
|
971
|
+
* Removes an app and every deployment it owns in the resolved branch.
|
|
972
|
+
*
|
|
973
|
+
* @param branchName Scopes the removal to this branch. When omitted the branch
|
|
974
|
+
* is inferred from the local Git branch, falling back to the production branch.
|
|
975
|
+
*/
|
|
976
|
+
async function runAppRemove(context, appName, projectRef, configTarget, branchName) {
|
|
970
977
|
ensurePreviewAppMode(context);
|
|
971
978
|
const compute = await resolveComputeManagementContext(context, configTarget, "remove");
|
|
972
979
|
appName = appName ?? compute.configAppName;
|
|
980
|
+
if (branchName !== void 0 && branchName.trim() === "") throw usageError("The --branch value cannot be empty", "app remove scopes the removal to the given branch; an empty --branch would silently fall back to the inferred (possibly production) branch.", "Pass a non-empty branch name, e.g. --branch <branch>, or omit --branch to use the inferred branch.", ["prisma-cli app remove --app <name> --branch <branch>"], "app");
|
|
973
981
|
const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
|
|
982
|
+
branch: branchName !== void 0 ? await resolveDeployBranch(context, branchName) : void 0,
|
|
974
983
|
commandName: "app remove",
|
|
975
984
|
projectDir: compute.projectDir
|
|
976
985
|
});
|
|
@@ -1424,7 +1433,7 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, requested
|
|
|
1424
1433
|
});
|
|
1425
1434
|
}
|
|
1426
1435
|
function deployNewAppRegion(configRegion) {
|
|
1427
|
-
return configRegion?.value
|
|
1436
|
+
return configRegion?.value;
|
|
1428
1437
|
}
|
|
1429
1438
|
async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
|
|
1430
1439
|
if (explicitAppName) {
|
|
@@ -1501,13 +1510,21 @@ function requireDeploymentForApp(deployments, deploymentId, appName) {
|
|
|
1501
1510
|
nextSteps: ["prisma-cli app list-deploys"]
|
|
1502
1511
|
});
|
|
1503
1512
|
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Resolves the app's live deployment from the app pointer, the provider's live
|
|
1515
|
+
* flag, then locally cached state.
|
|
1516
|
+
*
|
|
1517
|
+
* @returns the live deployment id, or null when no authoritative signal exists.
|
|
1518
|
+
* Callers must treat null as "not known to be live" rather than assuming the
|
|
1519
|
+
* newest deployment is live.
|
|
1520
|
+
*/
|
|
1504
1521
|
async function resolveCurrentLiveDeploymentId(context, projectId, app, deployments) {
|
|
1505
1522
|
if (app.liveDeploymentId && deployments.some((deployment) => deployment.id === app.liveDeploymentId)) return app.liveDeploymentId;
|
|
1506
1523
|
const providerLiveDeployment = deployments.find((deployment) => deployment.live === true);
|
|
1507
1524
|
if (providerLiveDeployment) return providerLiveDeployment.id;
|
|
1508
1525
|
const knownLiveDeploymentId = await context.stateStore.readKnownLiveDeployment(projectId, app.id);
|
|
1509
1526
|
if (knownLiveDeploymentId && deployments.some((deployment) => deployment.id === knownLiveDeploymentId)) return knownLiveDeploymentId;
|
|
1510
|
-
return
|
|
1527
|
+
return null;
|
|
1511
1528
|
}
|
|
1512
1529
|
function buildAppShowNextSteps(liveUrl, liveDeployment, deployments) {
|
|
1513
1530
|
const nextSteps = [];
|
|
@@ -1654,7 +1671,7 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1654
1671
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1655
1672
|
return withRemoteDeployBranch(provider, {
|
|
1656
1673
|
workspace,
|
|
1657
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1674
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, options.createProjectRegion)),
|
|
1658
1675
|
resolution: {
|
|
1659
1676
|
projectSource: "created",
|
|
1660
1677
|
targetName: projectName,
|
|
@@ -1705,14 +1722,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1705
1722
|
targetNameSource: "platform-mapping"
|
|
1706
1723
|
}
|
|
1707
1724
|
}, branch, context.runtime.signal);
|
|
1708
|
-
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
|
|
1725
|
+
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, options.createProjectRegion), branch, context.runtime.signal);
|
|
1709
1726
|
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1710
1727
|
}
|
|
1711
|
-
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1728
|
+
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, createProjectRegion) {
|
|
1712
1729
|
const setup = await promptForProjectSetupChoice({
|
|
1713
1730
|
context,
|
|
1714
1731
|
projects,
|
|
1715
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
|
|
1732
|
+
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, createProjectRegion),
|
|
1716
1733
|
cancel: {
|
|
1717
1734
|
why: "Deploy needs a Project before it can continue.",
|
|
1718
1735
|
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
@@ -1730,9 +1747,10 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
|
|
|
1730
1747
|
localPinAction: setup.action
|
|
1731
1748
|
};
|
|
1732
1749
|
}
|
|
1733
|
-
async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
|
|
1750
|
+
async function createProjectForDeploySetup(provider, projectName, workspace, signal, region) {
|
|
1734
1751
|
const created = await provider.createProject({
|
|
1735
1752
|
name: projectName,
|
|
1753
|
+
region,
|
|
1736
1754
|
signal
|
|
1737
1755
|
}).catch((error) => {
|
|
1738
1756
|
throw projectCreateFailedError(error, projectName, workspace, {
|
|
@@ -1748,6 +1766,7 @@ async function createProjectForDeploySetup(provider, projectName, workspace, sig
|
|
|
1748
1766
|
return {
|
|
1749
1767
|
id: created.id,
|
|
1750
1768
|
name: created.name,
|
|
1769
|
+
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {},
|
|
1751
1770
|
workspace
|
|
1752
1771
|
};
|
|
1753
1772
|
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
|
+
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
3
|
+
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
4
|
+
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
5
|
+
import { listFixtureWorkspaceProjects, listRealWorkspaceProjects } from "./project.js";
|
|
6
|
+
import { createManagementBucketProvider, normalizeBucket, normalizeKey } from "../lib/bucket/provider.js";
|
|
7
|
+
//#region src/controllers/bucket.ts
|
|
8
|
+
function isRealMode(context) {
|
|
9
|
+
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
10
|
+
}
|
|
11
|
+
async function runBucketList(context, flags) {
|
|
12
|
+
const { provider, target } = await requireBucketContext(context, flags, "bucket list");
|
|
13
|
+
const buckets = await provider.listBuckets({
|
|
14
|
+
projectId: target.project.id,
|
|
15
|
+
branchName: flags.branchName,
|
|
16
|
+
signal: context.runtime.signal
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
command: "bucket.list",
|
|
20
|
+
result: {
|
|
21
|
+
projectId: target.project.id,
|
|
22
|
+
projectName: target.project.name,
|
|
23
|
+
branchName: flags.branchName ?? null,
|
|
24
|
+
verboseContext: target,
|
|
25
|
+
buckets
|
|
26
|
+
},
|
|
27
|
+
warnings: [],
|
|
28
|
+
nextSteps: []
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function runBucketCreate(context, flags) {
|
|
32
|
+
const { provider, target } = await requireBucketContext(context, flags, "bucket create");
|
|
33
|
+
const bucket = await provider.createBucket({
|
|
34
|
+
projectId: target.project.id,
|
|
35
|
+
name: flags.name?.trim() || void 0,
|
|
36
|
+
branchGitName: flags.branchName,
|
|
37
|
+
signal: context.runtime.signal
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
command: "bucket.create",
|
|
41
|
+
result: {
|
|
42
|
+
projectId: target.project.id,
|
|
43
|
+
projectName: target.project.name,
|
|
44
|
+
verboseContext: target,
|
|
45
|
+
bucket
|
|
46
|
+
},
|
|
47
|
+
warnings: [],
|
|
48
|
+
nextSteps: []
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function runBucketDelete(context, bucketId) {
|
|
52
|
+
const id = bucketId.trim();
|
|
53
|
+
if (!id) throw new CliError({
|
|
54
|
+
code: "USAGE_ERROR",
|
|
55
|
+
domain: "bucket",
|
|
56
|
+
summary: "Bucket id required",
|
|
57
|
+
why: "Bucket deletion needs a bucket id.",
|
|
58
|
+
fix: "Pass the bucket id to delete.",
|
|
59
|
+
exitCode: 2,
|
|
60
|
+
nextSteps: ["prisma-cli bucket list"]
|
|
61
|
+
});
|
|
62
|
+
await (await requireBucketProviderOnly(context)).deleteBucket(id, { signal: context.runtime.signal });
|
|
63
|
+
return {
|
|
64
|
+
command: "bucket.delete",
|
|
65
|
+
result: { bucket: { id } },
|
|
66
|
+
warnings: [],
|
|
67
|
+
nextSteps: []
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function runBucketKeyList(context, bucketId) {
|
|
71
|
+
const id = bucketId.trim();
|
|
72
|
+
if (!id) throw new CliError({
|
|
73
|
+
code: "USAGE_ERROR",
|
|
74
|
+
domain: "bucket",
|
|
75
|
+
summary: "Bucket id required",
|
|
76
|
+
why: "Bucket key listing needs a bucket id.",
|
|
77
|
+
fix: "Pass the bucket id.",
|
|
78
|
+
exitCode: 2,
|
|
79
|
+
nextSteps: ["prisma-cli bucket list"]
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
command: "bucket.key.list",
|
|
83
|
+
result: {
|
|
84
|
+
bucketId: id,
|
|
85
|
+
keys: await (await requireBucketProviderOnly(context)).listKeys(id, { signal: context.runtime.signal })
|
|
86
|
+
},
|
|
87
|
+
warnings: [],
|
|
88
|
+
nextSteps: []
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function runBucketKeyCreate(context, bucketId, flags) {
|
|
92
|
+
const id = bucketId.trim();
|
|
93
|
+
if (!id) throw new CliError({
|
|
94
|
+
code: "USAGE_ERROR",
|
|
95
|
+
domain: "bucket",
|
|
96
|
+
summary: "Bucket id required",
|
|
97
|
+
why: "Bucket key creation needs a bucket id.",
|
|
98
|
+
fix: "Pass the bucket id.",
|
|
99
|
+
exitCode: 2,
|
|
100
|
+
nextSteps: ["prisma-cli bucket list"]
|
|
101
|
+
});
|
|
102
|
+
const role = resolveKeyRole(flags.role);
|
|
103
|
+
const created = await (await requireBucketProviderOnly(context)).createKey({
|
|
104
|
+
bucketId: id,
|
|
105
|
+
name: flags.name?.trim() || void 0,
|
|
106
|
+
role,
|
|
107
|
+
signal: context.runtime.signal
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
command: "bucket.key.create",
|
|
111
|
+
result: {
|
|
112
|
+
bucketId: id,
|
|
113
|
+
key: created.key,
|
|
114
|
+
secretAccessKey: created.secretAccessKey,
|
|
115
|
+
accessKeyId: created.accessKeyId,
|
|
116
|
+
endpoint: created.endpoint,
|
|
117
|
+
bucketName: created.bucketName
|
|
118
|
+
},
|
|
119
|
+
warnings: [],
|
|
120
|
+
nextSteps: []
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async function runBucketKeyDelete(context, bucketId, keyId) {
|
|
124
|
+
const bktId = bucketId.trim();
|
|
125
|
+
const kId = keyId.trim();
|
|
126
|
+
if (!bktId || !kId) throw new CliError({
|
|
127
|
+
code: "USAGE_ERROR",
|
|
128
|
+
domain: "bucket",
|
|
129
|
+
summary: "Bucket id and key id required",
|
|
130
|
+
why: "Bucket key deletion needs both a bucket id and a key id.",
|
|
131
|
+
fix: "Pass the bucket id and key id.",
|
|
132
|
+
exitCode: 2,
|
|
133
|
+
nextSteps: ["prisma-cli bucket key list <bucketId>"]
|
|
134
|
+
});
|
|
135
|
+
await (await requireBucketProviderOnly(context)).deleteKey(bktId, kId, { signal: context.runtime.signal });
|
|
136
|
+
return {
|
|
137
|
+
command: "bucket.key.delete",
|
|
138
|
+
result: { key: { id: kId } },
|
|
139
|
+
warnings: [],
|
|
140
|
+
nextSteps: []
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async function resolveBucketProvider(context) {
|
|
144
|
+
if (isRealMode(context)) {
|
|
145
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
146
|
+
if (!client) throw authRequiredError();
|
|
147
|
+
return createManagementBucketProvider(client);
|
|
148
|
+
}
|
|
149
|
+
return createFixtureBucketProvider(context);
|
|
150
|
+
}
|
|
151
|
+
async function requireBucketContext(context, flags, commandName) {
|
|
152
|
+
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
153
|
+
if (!workspace) throw workspaceRequiredError();
|
|
154
|
+
if (isRealMode(context)) {
|
|
155
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
156
|
+
if (!client) throw authRequiredError();
|
|
157
|
+
const targetResult = await resolveProjectTarget({
|
|
158
|
+
context,
|
|
159
|
+
workspace,
|
|
160
|
+
explicitProject: flags.projectRef,
|
|
161
|
+
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
|
|
162
|
+
commandName
|
|
163
|
+
});
|
|
164
|
+
if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
|
|
165
|
+
return {
|
|
166
|
+
provider: createManagementBucketProvider(client),
|
|
167
|
+
target: targetResult.value
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const targetResult = await resolveProjectTarget({
|
|
171
|
+
context,
|
|
172
|
+
workspace,
|
|
173
|
+
explicitProject: flags.projectRef,
|
|
174
|
+
listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
|
|
175
|
+
commandName
|
|
176
|
+
});
|
|
177
|
+
if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
|
|
178
|
+
return {
|
|
179
|
+
provider: createFixtureBucketProvider(context),
|
|
180
|
+
target: targetResult.value
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async function requireBucketProviderOnly(context) {
|
|
184
|
+
await requireAuthenticatedAuthState(context);
|
|
185
|
+
return resolveBucketProvider(context);
|
|
186
|
+
}
|
|
187
|
+
function createFixtureBucketProvider(context) {
|
|
188
|
+
return {
|
|
189
|
+
async listBuckets(options) {
|
|
190
|
+
return context.api.listBucketsForProject(options.projectId, options.branchName).map((bucket) => normalizeBucket(bucket));
|
|
191
|
+
},
|
|
192
|
+
async createBucket(options) {
|
|
193
|
+
const created = context.api.createBucket({
|
|
194
|
+
projectId: options.projectId,
|
|
195
|
+
name: options.name,
|
|
196
|
+
branchGitName: options.branchGitName
|
|
197
|
+
});
|
|
198
|
+
if (!created) throw branchNotFoundError(options.branchGitName ?? "");
|
|
199
|
+
return normalizeBucket(created);
|
|
200
|
+
},
|
|
201
|
+
async deleteBucket(bucketId) {
|
|
202
|
+
if (!context.api.deleteBucket(bucketId)) throw bucketNotFoundError(bucketId);
|
|
203
|
+
},
|
|
204
|
+
async listKeys(bucketId) {
|
|
205
|
+
if (!context.api.getBucket(bucketId)) throw bucketNotFoundError(bucketId);
|
|
206
|
+
return context.api.listBucketKeys(bucketId).map((key) => normalizeKey(key));
|
|
207
|
+
},
|
|
208
|
+
async createKey(options) {
|
|
209
|
+
const created = context.api.createBucketKey({
|
|
210
|
+
bucketId: options.bucketId,
|
|
211
|
+
name: options.name,
|
|
212
|
+
role: options.role
|
|
213
|
+
});
|
|
214
|
+
if (!created) throw bucketNotFoundError(options.bucketId);
|
|
215
|
+
return {
|
|
216
|
+
key: normalizeKey(created.key),
|
|
217
|
+
secretAccessKey: created.secretAccessKey,
|
|
218
|
+
accessKeyId: created.accessKeyId,
|
|
219
|
+
endpoint: created.endpoint,
|
|
220
|
+
bucketName: created.bucketName
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
async deleteKey(bucketId, keyId) {
|
|
224
|
+
if (!context.api.deleteBucketKey(bucketId, keyId)) throw keyNotFoundError(keyId, bucketId);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function resolveKeyRole(role) {
|
|
229
|
+
return role === "read" ? "read" : "read_write";
|
|
230
|
+
}
|
|
231
|
+
function branchNotFoundError(branchGitName) {
|
|
232
|
+
return new CliError({
|
|
233
|
+
code: "BRANCH_NOT_FOUND",
|
|
234
|
+
domain: "bucket",
|
|
235
|
+
summary: "Branch not found",
|
|
236
|
+
why: `No branch matched "${branchGitName}" in the resolved project.`,
|
|
237
|
+
fix: "Pass a branch git name from prisma-cli branch list.",
|
|
238
|
+
exitCode: 1,
|
|
239
|
+
nextSteps: ["prisma-cli branch list"]
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function bucketNotFoundError(bucketId) {
|
|
243
|
+
return new CliError({
|
|
244
|
+
code: "BUCKET_NOT_FOUND",
|
|
245
|
+
domain: "bucket",
|
|
246
|
+
summary: "Bucket not found",
|
|
247
|
+
why: `No bucket matched "${bucketId}".`,
|
|
248
|
+
fix: "Pass a bucket id from prisma-cli bucket list.",
|
|
249
|
+
exitCode: 1,
|
|
250
|
+
nextSteps: ["prisma-cli bucket list"]
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function keyNotFoundError(keyId, bucketId) {
|
|
254
|
+
return new CliError({
|
|
255
|
+
code: "BUCKET_KEY_NOT_FOUND",
|
|
256
|
+
domain: "bucket",
|
|
257
|
+
summary: "Bucket key not found",
|
|
258
|
+
why: `No key matched "${keyId}" for bucket "${bucketId}".`,
|
|
259
|
+
fix: "Pass a key id from prisma-cli bucket key list <bucketId>.",
|
|
260
|
+
exitCode: 1,
|
|
261
|
+
nextSteps: [`prisma-cli bucket key list ${bucketId}`]
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
export { runBucketCreate, runBucketDelete, runBucketKeyCreate, runBucketKeyDelete, runBucketKeyList, runBucketList };
|
|
@@ -104,7 +104,7 @@ async function runProjectShow(context, explicitProject) {
|
|
|
104
104
|
}) : []
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
async function runProjectCreate(context, projectName) {
|
|
107
|
+
async function runProjectCreate(context, projectName, options) {
|
|
108
108
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
109
109
|
if (!workspace) throw workspaceRequiredError();
|
|
110
110
|
if (!isValidProjectSetupName(projectName)) throw projectSetupNameRequiredError("project create");
|
|
@@ -115,6 +115,7 @@ async function runProjectCreate(context, projectName) {
|
|
|
115
115
|
const name = projectName.trim();
|
|
116
116
|
const created = await provider.createProject({
|
|
117
117
|
name,
|
|
118
|
+
region: options?.region,
|
|
118
119
|
signal: context.runtime.signal
|
|
119
120
|
}).catch((error) => {
|
|
120
121
|
throw projectCreateFailedError(error, name, workspace, {
|
|
@@ -125,7 +126,8 @@ async function runProjectCreate(context, projectName) {
|
|
|
125
126
|
});
|
|
126
127
|
const bindResult = await bindProjectToDirectory(context, workspace, {
|
|
127
128
|
id: created.id,
|
|
128
|
-
name: created.name
|
|
129
|
+
name: created.name,
|
|
130
|
+
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {}
|
|
129
131
|
}, "created");
|
|
130
132
|
if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
|
|
131
133
|
return {
|
|
@@ -704,6 +706,7 @@ async function listRealWorkspaceProjects(client, workspace, signal) {
|
|
|
704
706
|
id: project.id,
|
|
705
707
|
name: project.name,
|
|
706
708
|
..."url" in project && typeof project.url === "string" ? { url: project.url } : {},
|
|
709
|
+
..."defaultRegion" in project ? { defaultRegion: project.defaultRegion } : {},
|
|
707
710
|
slug: "slug" in project && typeof project.slug === "string" ? project.slug : null,
|
|
708
711
|
workspace: {
|
|
709
712
|
id: project.workspace.id,
|
|
@@ -22,12 +22,14 @@ function createAppProvider(client, options) {
|
|
|
22
22
|
async createProject(options) {
|
|
23
23
|
const projectResult = await sdk.createProject({
|
|
24
24
|
name: options.name,
|
|
25
|
+
region: options.region,
|
|
25
26
|
signal: options.signal
|
|
26
27
|
});
|
|
27
28
|
if (projectResult.isErr()) throw new Error(projectResult.error.message);
|
|
28
29
|
return {
|
|
29
30
|
id: projectResult.value.id,
|
|
30
|
-
name: projectResult.value.name
|
|
31
|
+
name: projectResult.value.name,
|
|
32
|
+
defaultRegion: projectResult.value.defaultRegion
|
|
31
33
|
};
|
|
32
34
|
},
|
|
33
35
|
async listApps(projectId, options) {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { CliError } from "../../shell/errors.js";
|
|
2
|
+
//#region src/lib/bucket/provider.ts
|
|
3
|
+
function createManagementBucketProvider(client) {
|
|
4
|
+
return {
|
|
5
|
+
async listBuckets(options) {
|
|
6
|
+
const buckets = [];
|
|
7
|
+
let cursor;
|
|
8
|
+
while (true) {
|
|
9
|
+
const result = await client.GET("/v1/buckets", {
|
|
10
|
+
params: { query: {
|
|
11
|
+
projectId: options.projectId,
|
|
12
|
+
branchGitName: options.branchName,
|
|
13
|
+
cursor
|
|
14
|
+
} },
|
|
15
|
+
signal: options.signal
|
|
16
|
+
});
|
|
17
|
+
if (result.error || !result.data) throw bucketApiError("Failed to list buckets", result.response, result.error);
|
|
18
|
+
const data = result.data;
|
|
19
|
+
buckets.push(...data.data);
|
|
20
|
+
if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
|
|
21
|
+
cursor = data.pagination.nextCursor;
|
|
22
|
+
}
|
|
23
|
+
return buckets.map(normalizeBucket);
|
|
24
|
+
},
|
|
25
|
+
async createBucket(options) {
|
|
26
|
+
const result = await client.POST("/v1/buckets", {
|
|
27
|
+
body: {
|
|
28
|
+
projectId: options.projectId,
|
|
29
|
+
...options.name ? { name: options.name } : {},
|
|
30
|
+
...options.branchGitName ? { branchGitName: options.branchGitName } : {}
|
|
31
|
+
},
|
|
32
|
+
signal: options.signal
|
|
33
|
+
});
|
|
34
|
+
if (result.error || !result.data) throw bucketApiError("Failed to create bucket", result.response, result.error);
|
|
35
|
+
const data = result.data;
|
|
36
|
+
return normalizeBucket(data.data);
|
|
37
|
+
},
|
|
38
|
+
async deleteBucket(bucketId, options) {
|
|
39
|
+
const result = await client.DELETE("/v1/buckets/{bucketId}", {
|
|
40
|
+
params: { path: { bucketId } },
|
|
41
|
+
signal: options?.signal
|
|
42
|
+
});
|
|
43
|
+
if (result.error) throw bucketApiError("Failed to delete bucket", result.response, result.error);
|
|
44
|
+
},
|
|
45
|
+
async listKeys(bucketId, options) {
|
|
46
|
+
const keys = [];
|
|
47
|
+
let cursor;
|
|
48
|
+
while (true) {
|
|
49
|
+
const result = await client.GET("/v1/buckets/{bucketId}/keys", {
|
|
50
|
+
params: {
|
|
51
|
+
path: { bucketId },
|
|
52
|
+
query: { cursor }
|
|
53
|
+
},
|
|
54
|
+
signal: options?.signal
|
|
55
|
+
});
|
|
56
|
+
if (result.error || !result.data) throw bucketApiError("Failed to list bucket keys", result.response, result.error);
|
|
57
|
+
const data = result.data;
|
|
58
|
+
keys.push(...data.data);
|
|
59
|
+
if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
|
|
60
|
+
cursor = data.pagination.nextCursor;
|
|
61
|
+
}
|
|
62
|
+
return keys.map(normalizeKey);
|
|
63
|
+
},
|
|
64
|
+
async createKey(options) {
|
|
65
|
+
const result = await client.POST("/v1/buckets/{bucketId}/keys", {
|
|
66
|
+
params: { path: { bucketId: options.bucketId } },
|
|
67
|
+
body: {
|
|
68
|
+
role: options.role,
|
|
69
|
+
...options.name ? { name: options.name } : {}
|
|
70
|
+
},
|
|
71
|
+
signal: options.signal
|
|
72
|
+
});
|
|
73
|
+
if (result.error || !result.data) throw bucketApiError("Failed to create bucket key", result.response, result.error);
|
|
74
|
+
const raw = result.data.data;
|
|
75
|
+
const secretAccessKey = raw.secretAccessKey;
|
|
76
|
+
const accessKeyId = raw.accessKeyId;
|
|
77
|
+
const endpoint = raw.endpoint;
|
|
78
|
+
const bucketName = raw.bucketName;
|
|
79
|
+
if (!secretAccessKey || !accessKeyId || !endpoint || !bucketName) throw new CliError({
|
|
80
|
+
code: "BUCKET_KEY_SECRET_MISSING",
|
|
81
|
+
domain: "bucket",
|
|
82
|
+
summary: "Created bucket key did not return credentials",
|
|
83
|
+
why: "Bucket key credentials are one-time-view secrets, but the Management API did not include them in this create response.",
|
|
84
|
+
fix: "Create another bucket key and store the returned credentials immediately.",
|
|
85
|
+
exitCode: 1,
|
|
86
|
+
nextSteps: [`prisma-cli bucket key create ${options.bucketId}`]
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
key: normalizeKey(raw),
|
|
90
|
+
secretAccessKey,
|
|
91
|
+
accessKeyId,
|
|
92
|
+
endpoint,
|
|
93
|
+
bucketName
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
async deleteKey(bucketId, keyId, options) {
|
|
97
|
+
const result = await client.DELETE("/v1/buckets/{bucketId}/keys/{keyId}", {
|
|
98
|
+
params: { path: {
|
|
99
|
+
bucketId,
|
|
100
|
+
keyId
|
|
101
|
+
} },
|
|
102
|
+
signal: options?.signal
|
|
103
|
+
});
|
|
104
|
+
if (result.error) throw bucketApiError("Failed to delete bucket key", result.response, result.error);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function normalizeBucket(raw) {
|
|
109
|
+
return {
|
|
110
|
+
id: raw.id,
|
|
111
|
+
name: raw.name,
|
|
112
|
+
status: raw.status,
|
|
113
|
+
branchId: raw.branchId,
|
|
114
|
+
createdAt: raw.createdAt
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function normalizeKey(raw) {
|
|
118
|
+
return {
|
|
119
|
+
id: raw.id,
|
|
120
|
+
name: raw.name,
|
|
121
|
+
role: raw.role,
|
|
122
|
+
valueHint: raw.valueHint,
|
|
123
|
+
createdAt: raw.createdAt
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function bucketApiError(summary, response, error) {
|
|
127
|
+
const status = response?.status ?? 0;
|
|
128
|
+
return new CliError({
|
|
129
|
+
code: error?.error?.code ?? "BUCKET_API_ERROR",
|
|
130
|
+
domain: "bucket",
|
|
131
|
+
summary,
|
|
132
|
+
why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
|
|
133
|
+
fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
|
|
134
|
+
exitCode: 1,
|
|
135
|
+
nextSteps: []
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { createManagementBucketProvider, normalizeBucket, normalizeKey };
|
|
@@ -379,7 +379,8 @@ function toProjectSummary(project) {
|
|
|
379
379
|
return {
|
|
380
380
|
id: project.id,
|
|
381
381
|
name: project.name,
|
|
382
|
-
...project.url ? { url: project.url } : {}
|
|
382
|
+
...project.url ? { url: project.url } : {},
|
|
383
|
+
...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
|
|
383
384
|
};
|
|
384
385
|
}
|
|
385
386
|
//#endregion
|
|
@@ -82,7 +82,8 @@ function toProjectSummary(project) {
|
|
|
82
82
|
return {
|
|
83
83
|
id: project.id,
|
|
84
84
|
name: project.name,
|
|
85
|
-
...project.url ? { url: project.url } : {}
|
|
85
|
+
...project.url ? { url: project.url } : {},
|
|
86
|
+
...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
|
|
86
87
|
};
|
|
87
88
|
}
|
|
88
89
|
function projectSetupNameRequiredError(command) {
|
package/dist/presenters/app.js
CHANGED
|
@@ -48,6 +48,11 @@ function renderAppDeploy(context, descriptor, result, options) {
|
|
|
48
48
|
label: "Logs",
|
|
49
49
|
value: logsCommand
|
|
50
50
|
},
|
|
51
|
+
...result.deploySettings.region && result.deploySettings.regionSource === null ? [{
|
|
52
|
+
label: "Region",
|
|
53
|
+
value: result.deploySettings.region,
|
|
54
|
+
origin: result.project.defaultRegion != null ? "project default" : "platform default — pass --region to choose"
|
|
55
|
+
}] : [],
|
|
51
56
|
...deployUsedComputeConfig(result) ? [] : [{
|
|
52
57
|
label: "Config",
|
|
53
58
|
value: resolvePrismaCliPackageCommandSync(context.runtime.cwd, ["init"])
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
2
|
+
import { formatColumns, renderSummaryLine } from "../shell/ui.js";
|
|
3
|
+
import { renderMutate, serializeList } from "../output/patterns.js";
|
|
4
|
+
import { renderResolvedProjectContextBlock, stripVerboseContext } from "./verbose-context.js";
|
|
5
|
+
//#region src/presenters/bucket.ts
|
|
6
|
+
function renderBucketList(context, descriptor, result) {
|
|
7
|
+
const ui = context.ui;
|
|
8
|
+
const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing object-store buckets for the resolved project.")}`, ""];
|
|
9
|
+
const rail = ui.dim("│");
|
|
10
|
+
lines.push(`${rail} ${ui.accent("project:")} ${result.projectName}`);
|
|
11
|
+
if (result.branchName) lines.push(`${rail} ${ui.accent("branch:")} ${result.branchName}`);
|
|
12
|
+
lines.push(rail);
|
|
13
|
+
if (result.buckets.length === 0) {
|
|
14
|
+
lines.push(`${rail} ${ui.dim("No buckets found.")}`);
|
|
15
|
+
lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
|
|
16
|
+
return lines;
|
|
17
|
+
}
|
|
18
|
+
const rows = result.buckets.map((bucket) => [
|
|
19
|
+
bucket.name,
|
|
20
|
+
bucket.id,
|
|
21
|
+
bucket.status,
|
|
22
|
+
bucket.branchId ?? "unscoped",
|
|
23
|
+
bucket.createdAt
|
|
24
|
+
]);
|
|
25
|
+
const widths = [
|
|
26
|
+
Math.max(4, ...rows.map((row) => row[0].length)),
|
|
27
|
+
Math.max(2, ...rows.map((row) => row[1].length)),
|
|
28
|
+
Math.max(6, ...rows.map((row) => row[2].length)),
|
|
29
|
+
Math.max(6, ...rows.map((row) => row[3].length)),
|
|
30
|
+
Math.max(7, ...rows.map((row) => row[4].length))
|
|
31
|
+
];
|
|
32
|
+
lines.push(`${rail} ${ui.accent(formatColumns([
|
|
33
|
+
"Name",
|
|
34
|
+
"Id",
|
|
35
|
+
"Status",
|
|
36
|
+
"Branch",
|
|
37
|
+
"Created"
|
|
38
|
+
], widths))}`);
|
|
39
|
+
for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
|
|
40
|
+
lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
|
|
41
|
+
return lines;
|
|
42
|
+
}
|
|
43
|
+
function serializeBucketList(result) {
|
|
44
|
+
return {
|
|
45
|
+
context: {
|
|
46
|
+
project: result.projectName,
|
|
47
|
+
...result.branchName ? { branch: result.branchName } : {}
|
|
48
|
+
},
|
|
49
|
+
items: result.buckets.map((bucket) => ({
|
|
50
|
+
name: bucket.name,
|
|
51
|
+
id: bucket.id,
|
|
52
|
+
status: bucket.status
|
|
53
|
+
})),
|
|
54
|
+
count: result.buckets.length,
|
|
55
|
+
projectId: result.projectId,
|
|
56
|
+
branchName: result.branchName,
|
|
57
|
+
buckets: result.buckets
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function renderBucketCreate(context, _descriptor, result) {
|
|
61
|
+
const ui = context.ui;
|
|
62
|
+
return ["Creating bucket...", renderSummaryLine(ui, "success", `Created bucket "${result.bucket.name}" in ${formatBucketTarget(result.projectName, result.bucket.branchId)}.`)];
|
|
63
|
+
}
|
|
64
|
+
function serializeBucketCreate(result) {
|
|
65
|
+
return stripVerboseContext(result);
|
|
66
|
+
}
|
|
67
|
+
function renderBucketDelete(context, descriptor, result) {
|
|
68
|
+
return renderMutate({
|
|
69
|
+
title: "Deleting object-store bucket.",
|
|
70
|
+
descriptor,
|
|
71
|
+
context: [{
|
|
72
|
+
key: "bucket",
|
|
73
|
+
value: result.bucket.id,
|
|
74
|
+
tone: "dim"
|
|
75
|
+
}],
|
|
76
|
+
operationDescription: "Deleting bucket",
|
|
77
|
+
operationCount: 1,
|
|
78
|
+
details: ["Bucket and all its access keys were removed."]
|
|
79
|
+
}, context.ui);
|
|
80
|
+
}
|
|
81
|
+
function serializeBucketDelete(result) {
|
|
82
|
+
return { bucket: result.bucket };
|
|
83
|
+
}
|
|
84
|
+
function renderBucketKeyList(context, descriptor, result) {
|
|
85
|
+
const ui = context.ui;
|
|
86
|
+
const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing access keys for bucket.")}`, ""];
|
|
87
|
+
const rail = ui.dim("│");
|
|
88
|
+
lines.push(`${rail} ${ui.accent("bucket:")} ${result.bucketId}`);
|
|
89
|
+
lines.push(rail);
|
|
90
|
+
if (result.keys.length === 0) {
|
|
91
|
+
lines.push(`${rail} ${ui.dim("No keys found.")}`);
|
|
92
|
+
return lines;
|
|
93
|
+
}
|
|
94
|
+
const rows = result.keys.map((key) => [
|
|
95
|
+
key.name,
|
|
96
|
+
key.id,
|
|
97
|
+
key.role,
|
|
98
|
+
key.valueHint,
|
|
99
|
+
key.createdAt
|
|
100
|
+
]);
|
|
101
|
+
const widths = [
|
|
102
|
+
Math.max(4, ...rows.map((row) => row[0].length)),
|
|
103
|
+
Math.max(2, ...rows.map((row) => row[1].length)),
|
|
104
|
+
Math.max(4, ...rows.map((row) => row[2].length)),
|
|
105
|
+
Math.max(4, ...rows.map((row) => row[3].length)),
|
|
106
|
+
Math.max(7, ...rows.map((row) => row[4].length))
|
|
107
|
+
];
|
|
108
|
+
lines.push(`${rail} ${ui.accent(formatColumns([
|
|
109
|
+
"Name",
|
|
110
|
+
"Id",
|
|
111
|
+
"Role",
|
|
112
|
+
"Hint",
|
|
113
|
+
"Created"
|
|
114
|
+
], widths))}`);
|
|
115
|
+
for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
|
|
116
|
+
return lines;
|
|
117
|
+
}
|
|
118
|
+
function serializeBucketKeyList(result) {
|
|
119
|
+
return {
|
|
120
|
+
...serializeList({
|
|
121
|
+
context: { bucket: result.bucketId },
|
|
122
|
+
items: result.keys.map((key) => ({
|
|
123
|
+
noun: "key",
|
|
124
|
+
label: key.name,
|
|
125
|
+
id: key.id,
|
|
126
|
+
status: null
|
|
127
|
+
}))
|
|
128
|
+
}),
|
|
129
|
+
bucketId: result.bucketId,
|
|
130
|
+
keys: result.keys
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function renderBucketKeyCreateStdout(_context, _descriptor, result) {
|
|
134
|
+
return [
|
|
135
|
+
`S3_ENDPOINT=${result.endpoint}`,
|
|
136
|
+
`S3_ACCESS_KEY_ID=${result.accessKeyId}`,
|
|
137
|
+
`S3_SECRET_ACCESS_KEY=${result.secretAccessKey}`,
|
|
138
|
+
`S3_BUCKET=${result.bucketName}`
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
function renderBucketKeyCreate(context, _descriptor, result) {
|
|
142
|
+
const ui = context.ui;
|
|
143
|
+
return [
|
|
144
|
+
"Creating bucket key...",
|
|
145
|
+
renderSummaryLine(ui, "success", `Created key "${result.key.name}" for bucket "${result.bucketName}".`),
|
|
146
|
+
" The credentials below are shown once — copy them now.",
|
|
147
|
+
" Set these environment variables to use this bucket:"
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
function serializeBucketKeyCreate(result) {
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
function renderBucketKeyDelete(context, descriptor, result) {
|
|
154
|
+
return renderMutate({
|
|
155
|
+
title: "Deleting bucket access key.",
|
|
156
|
+
descriptor,
|
|
157
|
+
context: [{
|
|
158
|
+
key: "key",
|
|
159
|
+
value: result.key.id,
|
|
160
|
+
tone: "dim"
|
|
161
|
+
}],
|
|
162
|
+
operationDescription: "Deleting bucket key",
|
|
163
|
+
operationCount: 1,
|
|
164
|
+
details: ["The access key was revoked and removed."]
|
|
165
|
+
}, context.ui);
|
|
166
|
+
}
|
|
167
|
+
function serializeBucketKeyDelete(result) {
|
|
168
|
+
return { key: result.key };
|
|
169
|
+
}
|
|
170
|
+
function formatBucketTarget(projectName, branchId) {
|
|
171
|
+
return branchId ? `${projectName} / ${branchId}` : projectName;
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { renderBucketCreate, renderBucketDelete, renderBucketKeyCreate, renderBucketKeyCreateStdout, renderBucketKeyDelete, renderBucketKeyList, renderBucketList, serializeBucketCreate, serializeBucketDelete, serializeBucketKeyCreate, serializeBucketKeyDelete, serializeBucketKeyList, serializeBucketList };
|
|
@@ -17,9 +17,13 @@ function renderProjectList(context, descriptor, result) {
|
|
|
17
17
|
return lines;
|
|
18
18
|
}
|
|
19
19
|
const nameWidth = Math.max(4, ...result.projects.map((project) => stringWidth(project.name)));
|
|
20
|
+
const idWidth = Math.max(2, ...result.projects.map((project) => project.id.length));
|
|
20
21
|
lines.push(rail);
|
|
21
|
-
lines.push(`${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent("id")}`);
|
|
22
|
-
for (const project of result.projects)
|
|
22
|
+
lines.push(`${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent(padDisplay("id", idWidth))} ${ui.accent("region")}`);
|
|
23
|
+
for (const project of result.projects) {
|
|
24
|
+
const region = project.defaultRegion ? project.defaultRegion : ui.dim("none");
|
|
25
|
+
lines.push(`${rail} ${padDisplay(project.name, nameWidth)} ${padDisplay(project.id, idWidth)} ${region}`);
|
|
26
|
+
}
|
|
23
27
|
if (result.localBinding?.status === "not-linked" || result.localBinding?.status === "invalid") lines.push(...renderNextSteps(["Link an existing Project you choose: prisma-cli project link <id-or-name>", "Create a new Project: prisma-cli project create <name>"]));
|
|
24
28
|
return lines;
|
|
25
29
|
}
|
|
@@ -241,6 +245,7 @@ function renderBoundProjectShow(context, descriptor, result) {
|
|
|
241
245
|
lines.push(rail);
|
|
242
246
|
lines.push(`${rail} ${ui.dim("→")} ${ui.link(result.project.url)}`);
|
|
243
247
|
}
|
|
248
|
+
if (result.project.defaultRegion) lines.push(`${rail} ${ui.accent(padDisplay("region", keyWidth))} ${ui.dim(result.project.defaultRegion)}`);
|
|
244
249
|
lines.push(...renderResolvedProjectContextBlock(context.ui, {
|
|
245
250
|
workspace: result.workspace,
|
|
246
251
|
project: result.project,
|
|
@@ -249,6 +249,105 @@ const DESCRIPTORS = [
|
|
|
249
249
|
"prisma-cli database connection create db_123"
|
|
250
250
|
]
|
|
251
251
|
},
|
|
252
|
+
{
|
|
253
|
+
id: "bucket",
|
|
254
|
+
path: ["prisma", "bucket"],
|
|
255
|
+
description: "Manage object-store buckets for a project",
|
|
256
|
+
examples: [
|
|
257
|
+
"prisma-cli bucket list",
|
|
258
|
+
"prisma-cli bucket create",
|
|
259
|
+
"prisma-cli bucket key create bkt_123"
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: "bucket.list",
|
|
264
|
+
path: [
|
|
265
|
+
"prisma",
|
|
266
|
+
"bucket",
|
|
267
|
+
"list"
|
|
268
|
+
],
|
|
269
|
+
description: "List object-store buckets for the resolved project",
|
|
270
|
+
examples: [
|
|
271
|
+
"prisma-cli bucket list",
|
|
272
|
+
"prisma-cli bucket list --branch preview",
|
|
273
|
+
"prisma-cli bucket list --json"
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: "bucket.create",
|
|
278
|
+
path: [
|
|
279
|
+
"prisma",
|
|
280
|
+
"bucket",
|
|
281
|
+
"create"
|
|
282
|
+
],
|
|
283
|
+
description: "Create an object-store bucket",
|
|
284
|
+
examples: [
|
|
285
|
+
"prisma-cli bucket create",
|
|
286
|
+
"prisma-cli bucket create --name my-store",
|
|
287
|
+
"prisma-cli bucket create --branch preview --json"
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: "bucket.delete",
|
|
292
|
+
path: [
|
|
293
|
+
"prisma",
|
|
294
|
+
"bucket",
|
|
295
|
+
"delete"
|
|
296
|
+
],
|
|
297
|
+
description: "Delete a bucket and all its access keys",
|
|
298
|
+
examples: ["prisma-cli bucket delete bkt_123"]
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
id: "bucket.key",
|
|
302
|
+
path: [
|
|
303
|
+
"prisma",
|
|
304
|
+
"bucket",
|
|
305
|
+
"key"
|
|
306
|
+
],
|
|
307
|
+
description: "Manage access keys for an object-store bucket",
|
|
308
|
+
examples: [
|
|
309
|
+
"prisma-cli bucket key list bkt_123",
|
|
310
|
+
"prisma-cli bucket key create bkt_123",
|
|
311
|
+
"prisma-cli bucket key delete bkt_123 bkey_456"
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: "bucket.key.list",
|
|
316
|
+
path: [
|
|
317
|
+
"prisma",
|
|
318
|
+
"bucket",
|
|
319
|
+
"key",
|
|
320
|
+
"list"
|
|
321
|
+
],
|
|
322
|
+
description: "List access keys for a bucket",
|
|
323
|
+
examples: ["prisma-cli bucket key list bkt_123", "prisma-cli bucket key list bkt_123 --json"]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: "bucket.key.create",
|
|
327
|
+
path: [
|
|
328
|
+
"prisma",
|
|
329
|
+
"bucket",
|
|
330
|
+
"key",
|
|
331
|
+
"create"
|
|
332
|
+
],
|
|
333
|
+
description: "Create a bucket access key and print its one-time credentials",
|
|
334
|
+
examples: [
|
|
335
|
+
"prisma-cli bucket key create bkt_123",
|
|
336
|
+
"prisma-cli bucket key create bkt_123 --role read",
|
|
337
|
+
"prisma-cli bucket key create bkt_123 --name ci-key --role read_write"
|
|
338
|
+
]
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "bucket.key.delete",
|
|
342
|
+
path: [
|
|
343
|
+
"prisma",
|
|
344
|
+
"bucket",
|
|
345
|
+
"key",
|
|
346
|
+
"delete"
|
|
347
|
+
],
|
|
348
|
+
description: "Revoke and delete a bucket access key",
|
|
349
|
+
examples: ["prisma-cli bucket key delete bkt_123 bkey_456"]
|
|
350
|
+
},
|
|
252
351
|
{
|
|
253
352
|
id: "git",
|
|
254
353
|
path: ["prisma", "git"],
|
|
@@ -699,8 +798,12 @@ const DESCRIPTORS = [
|
|
|
699
798
|
"app",
|
|
700
799
|
"remove"
|
|
701
800
|
],
|
|
702
|
-
description: "Remove the app from the
|
|
703
|
-
examples: [
|
|
801
|
+
description: "Remove the app from the resolved branch",
|
|
802
|
+
examples: [
|
|
803
|
+
"prisma-cli app remove --app hello-world",
|
|
804
|
+
"prisma-cli app remove --app hello-world --yes",
|
|
805
|
+
"prisma-cli app remove --app hello-world --branch feature/foo --yes"
|
|
806
|
+
]
|
|
704
807
|
},
|
|
705
808
|
{
|
|
706
809
|
id: "project.env",
|
|
@@ -23,7 +23,8 @@ function toProjectSummary(project) {
|
|
|
23
23
|
return {
|
|
24
24
|
id: project.id,
|
|
25
25
|
name: project.name,
|
|
26
|
-
...project.url ? { url: project.url } : {}
|
|
26
|
+
...project.url ? { url: project.url } : {},
|
|
27
|
+
...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/cli",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.29",
|
|
4
4
|
"description": "Command-line interface for the Prisma Developer Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@clack/prompts": "^1.5.0",
|
|
47
47
|
"@prisma/compute-sdk": "0.34.0",
|
|
48
48
|
"@prisma/credentials-store": "^7.8.0",
|
|
49
|
-
"@prisma/management-api-sdk": "^1.
|
|
49
|
+
"@prisma/management-api-sdk": "^1.50.0",
|
|
50
50
|
"better-result": "^2.9.2",
|
|
51
51
|
"colorette": "^2.0.20",
|
|
52
52
|
"commander": "^14.0.3",
|