@prisma/cli 3.0.0-beta.3 → 3.0.0-beta.30
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 +6 -15
- package/dist/adapters/local-state.js +15 -4
- package/dist/adapters/mock-api.js +244 -0
- package/dist/adapters/token-storage.js +335 -34
- package/dist/cli.js +7 -7
- package/dist/cli2.js +24 -5
- package/dist/commands/agent/index.js +60 -0
- package/dist/commands/app/index.js +91 -61
- package/dist/commands/auth/index.js +55 -2
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/bucket/index.js +123 -0
- package/dist/commands/build/index.js +29 -0
- package/dist/commands/database/index.js +249 -0
- package/dist/commands/env.js +8 -4
- package/dist/commands/feedback/index.js +20 -0
- package/dist/commands/git/index.js +1 -1
- package/dist/commands/init/index.js +33 -0
- package/dist/commands/project/index.js +54 -5
- package/dist/controllers/agent-setup.js +52 -0
- package/dist/controllers/agent.js +228 -0
- package/dist/controllers/app-env-api.js +55 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +227 -104
- package/dist/controllers/app.js +746 -306
- package/dist/controllers/auth.js +247 -3
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/bucket.js +278 -0
- package/dist/controllers/build.js +88 -0
- package/dist/controllers/database.js +567 -0
- package/dist/controllers/feedback.js +86 -0
- package/dist/controllers/init.js +753 -0
- package/dist/controllers/project.js +377 -22
- package/dist/controllers/select-prompt-port.js +1 -0
- package/dist/lib/agent/cli-command.js +20 -0
- package/dist/lib/agent/constants.js +12 -0
- package/dist/lib/agent/package-manager.js +99 -0
- package/dist/lib/agent/setup-status.js +83 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +137 -88
- package/dist/lib/app/branch-database-api.js +102 -0
- package/dist/lib/app/branch-database-deploy.js +326 -0
- package/dist/lib/app/branch-database.js +216 -0
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +83 -0
- package/dist/lib/app/bun-project.js +3 -4
- package/dist/lib/app/compute-config.js +145 -0
- package/dist/lib/app/deploy-plan.js +59 -0
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
- package/dist/lib/app/env-config.js +1 -1
- package/dist/lib/app/env-file.js +82 -0
- package/dist/lib/app/env-vars.js +28 -2
- package/dist/lib/app/local-dev.js +3 -60
- package/dist/lib/app/production-deploy-gate.js +162 -0
- package/dist/lib/app/read-branch.js +30 -0
- package/dist/lib/auth/auth-ops.js +10 -4
- package/dist/lib/auth/guard.js +4 -1
- package/dist/lib/auth/login.js +33 -26
- package/dist/lib/auth/recipient.js +42 -0
- package/dist/lib/bucket/provider.js +139 -0
- package/dist/lib/database/provider.js +378 -0
- package/dist/lib/diagnostics.js +15 -0
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +53 -0
- package/dist/lib/git/local-status.js +57 -0
- package/dist/lib/project/interactive-setup.js +5 -4
- package/dist/lib/project/local-pin.js +171 -41
- package/dist/lib/project/provider.js +92 -0
- package/dist/lib/project/resolution.js +199 -48
- package/dist/lib/project/setup.js +67 -20
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/agent.js +74 -0
- package/dist/presenters/app-env.js +149 -14
- package/dist/presenters/app.js +208 -27
- package/dist/presenters/auth.js +99 -2
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/bucket.js +174 -0
- package/dist/presenters/database.js +448 -0
- package/dist/presenters/feedback.js +26 -0
- package/dist/presenters/init.js +30 -0
- package/dist/presenters/project.js +139 -27
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/cli-command.js +12 -0
- package/dist/shell/command-arguments.js +7 -1
- package/dist/shell/command-meta.js +458 -17
- package/dist/shell/command-runner.js +58 -18
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +56 -1
- package/dist/shell/help.js +31 -20
- package/dist/shell/output.js +72 -1
- package/dist/shell/prompt.js +12 -5
- package/dist/shell/runtime.js +8 -4
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +2 -2
- package/dist/use-cases/auth.js +68 -1
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/dist/use-cases/project.js +2 -1
- package/package.json +21 -4
- package/dist/lib/app/preview-build.js +0 -312
- package/dist/lib/app/preview-interaction.js +0 -5
|
@@ -1,42 +1,38 @@
|
|
|
1
1
|
import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
|
+
import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
|
|
3
|
+
import { readEnvFileAssignments } from "../lib/app/env-file.js";
|
|
4
|
+
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
|
|
2
5
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
3
|
-
import {
|
|
6
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
4
7
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
5
8
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
6
|
-
import {
|
|
9
|
+
import { apiCallError, findVariableByNaturalKey, toMetadata } from "./app-env-api.js";
|
|
10
|
+
import { runEnvAddFile, runEnvUpdateFile } from "./app-env-file.js";
|
|
7
11
|
//#region src/controllers/app-env.ts
|
|
8
|
-
function defaultRoleScope() {
|
|
9
|
-
return {
|
|
10
|
-
kind: "role",
|
|
11
|
-
role: "production"
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
12
|
async function runEnvAdd(context, rawAssignment, flags) {
|
|
15
|
-
const
|
|
13
|
+
const source = resolveEnvWriteSource(rawAssignment, flags.filePath, "add");
|
|
16
14
|
const scope = resolveEnvScope(flags, {
|
|
17
15
|
requireExplicit: true,
|
|
18
16
|
command: "add"
|
|
19
17
|
});
|
|
20
|
-
if (!scope) throw usageError(`prisma-cli project env add requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [
|
|
21
|
-
const
|
|
18
|
+
if (!scope) throw usageError(`prisma-cli project env add requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", ["prisma-cli project env add KEY=value --role production"], "app");
|
|
19
|
+
const input = await resolveEnvWriteInput(context, source, "add");
|
|
20
|
+
const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env add");
|
|
22
21
|
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
23
22
|
createBranchIfMissing: true,
|
|
24
23
|
signal: context.runtime.signal
|
|
25
24
|
});
|
|
26
|
-
if (
|
|
25
|
+
if (input.kind === "file") return runEnvAddFile(context, client, projectId, resolved, input.filePath, input.assignments, verboseContext);
|
|
26
|
+
if (await findVariableByNaturalKey(client, projectId, input.key, resolved, context.runtime.signal)) throw new CliError({
|
|
27
27
|
code: "ENV_VARIABLE_ALREADY_EXISTS",
|
|
28
28
|
domain: "app",
|
|
29
|
-
summary: `Variable "${key}" already exists in ${formatScopeLabel(scope)}`,
|
|
29
|
+
summary: `Variable "${input.key}" already exists in ${formatScopeLabel(scope)}`,
|
|
30
30
|
why: "A variable with this key already exists in the targeted scope.",
|
|
31
31
|
fix: "Use `prisma-cli project env update` to change an existing variable's value.",
|
|
32
32
|
exitCode: 1,
|
|
33
|
-
nextSteps: [`prisma-cli project env update ${key}=<new-value> ${formatScopeFlag(scope)}`]
|
|
33
|
+
nextSteps: [`prisma-cli project env update ${input.key}=<new-value> ${formatScopeFlag(scope)}`]
|
|
34
34
|
});
|
|
35
|
-
const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(client, projectId, key, {
|
|
36
|
-
scope: {
|
|
37
|
-
kind: "role",
|
|
38
|
-
role: "preview"
|
|
39
|
-
},
|
|
35
|
+
const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(client, projectId, input.key, {
|
|
40
36
|
descriptor: {
|
|
41
37
|
kind: "role",
|
|
42
38
|
role: "preview"
|
|
@@ -45,22 +41,23 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
45
41
|
class: "preview",
|
|
46
42
|
branchId: null
|
|
47
43
|
}
|
|
48
|
-
}, context.runtime.signal) ? [`Variable "${key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
44
|
+
}, context.runtime.signal) ? [`Variable "${input.key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
49
45
|
const { data, error, response } = await client.POST("/v1/environment-variables", {
|
|
50
46
|
body: {
|
|
51
47
|
projectId,
|
|
52
48
|
class: resolved.apiTarget.class,
|
|
53
49
|
...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
|
|
54
|
-
key,
|
|
55
|
-
value
|
|
50
|
+
key: input.key,
|
|
51
|
+
value: input.value
|
|
56
52
|
},
|
|
57
53
|
signal: context.runtime.signal
|
|
58
54
|
});
|
|
59
|
-
if (error || !data) throw apiCallError(`Failed to add ${key}`, response, error);
|
|
55
|
+
if (error || !data) throw apiCallError(`Failed to add ${input.key}`, response, error);
|
|
60
56
|
return {
|
|
61
57
|
command: "project.env.add",
|
|
62
58
|
result: {
|
|
63
59
|
projectId,
|
|
60
|
+
verboseContext,
|
|
64
61
|
scope: resolved.descriptor,
|
|
65
62
|
variable: toMetadata(data.data, resolved.descriptor)
|
|
66
63
|
},
|
|
@@ -69,37 +66,40 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
69
66
|
};
|
|
70
67
|
}
|
|
71
68
|
async function runEnvUpdate(context, rawAssignment, flags) {
|
|
72
|
-
const
|
|
69
|
+
const source = resolveEnvWriteSource(rawAssignment, flags.filePath, "update");
|
|
73
70
|
const scope = resolveEnvScope(flags, {
|
|
74
71
|
requireExplicit: true,
|
|
75
72
|
command: "update"
|
|
76
73
|
});
|
|
77
|
-
if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [
|
|
78
|
-
const
|
|
74
|
+
if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", ["prisma-cli project env update KEY=value --role production"], "app");
|
|
75
|
+
const input = await resolveEnvWriteInput(context, source, "update");
|
|
76
|
+
const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env update");
|
|
79
77
|
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
80
78
|
createBranchIfMissing: false,
|
|
81
79
|
signal: context.runtime.signal
|
|
82
80
|
});
|
|
83
|
-
|
|
81
|
+
if (input.kind === "file") return runEnvUpdateFile(context, client, projectId, resolved, input.filePath, input.assignments, verboseContext);
|
|
82
|
+
const existing = await findVariableByNaturalKey(client, projectId, input.key, resolved, context.runtime.signal);
|
|
84
83
|
if (!existing) throw new CliError({
|
|
85
84
|
code: "ENV_VARIABLE_NOT_FOUND",
|
|
86
85
|
domain: "app",
|
|
87
|
-
summary: `Variable "${key}" not found in ${formatScopeLabel(scope)}`,
|
|
86
|
+
summary: `Variable "${input.key}" not found in ${formatScopeLabel(scope)}`,
|
|
88
87
|
why: "No variable with this key exists in the targeted scope.",
|
|
89
88
|
fix: "Use `prisma-cli project env add` to create a new variable.",
|
|
90
89
|
exitCode: 1,
|
|
91
|
-
nextSteps: [`prisma-cli project env add ${key}=<value> ${formatScopeFlag(scope)}`]
|
|
90
|
+
nextSteps: [`prisma-cli project env add ${input.key}=<value> ${formatScopeFlag(scope)}`]
|
|
92
91
|
});
|
|
93
92
|
const { data, error, response } = await client.PATCH("/v1/environment-variables/{envVarId}", {
|
|
94
93
|
params: { path: { envVarId: existing.id } },
|
|
95
|
-
body: { value },
|
|
94
|
+
body: { value: input.value },
|
|
96
95
|
signal: context.runtime.signal
|
|
97
96
|
});
|
|
98
|
-
if (error || !data) throw apiCallError(`Failed to update value for ${key}`, response, error);
|
|
97
|
+
if (error || !data) throw apiCallError(`Failed to update value for ${input.key}`, response, error);
|
|
99
98
|
return {
|
|
100
99
|
command: "project.env.update",
|
|
101
100
|
result: {
|
|
102
101
|
projectId,
|
|
102
|
+
verboseContext,
|
|
103
103
|
scope: resolved.descriptor,
|
|
104
104
|
variable: toMetadata(data.data, resolved.descriptor)
|
|
105
105
|
},
|
|
@@ -107,26 +107,58 @@ async function runEnvUpdate(context, rawAssignment, flags) {
|
|
|
107
107
|
nextSteps: []
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
+
function resolveEnvWriteSource(rawAssignment, filePath, command) {
|
|
111
|
+
if (filePath !== void 0 && rawAssignment !== void 0) throw usageError(`prisma-cli project env ${command} accepts either KEY=VALUE or --file`, "The command received both a positional assignment and a dotenv file path.", "Pass one input source.", [`prisma-cli project env ${command} KEY=value --role preview`, `prisma-cli project env ${command} --file .env --role preview`], "app");
|
|
112
|
+
if (filePath !== void 0) {
|
|
113
|
+
if (filePath.length === 0) throw usageError(`prisma-cli project env ${command} --file requires a path`, "The --file flag was passed without a file path.", "Pass a readable dotenv file path.", [`prisma-cli project env ${command} --file .env --role preview`], "app");
|
|
114
|
+
return {
|
|
115
|
+
kind: "file",
|
|
116
|
+
filePath
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (rawAssignment === void 0) throw usageError(`prisma-cli project env ${command} requires KEY=VALUE or --file`, "No environment variable input was supplied.", "Pass a single KEY=VALUE assignment or a dotenv file path.", [`prisma-cli project env ${command} KEY=value --role preview`, `prisma-cli project env ${command} --file .env --role preview`], "app");
|
|
120
|
+
return {
|
|
121
|
+
kind: "single",
|
|
122
|
+
rawAssignment
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function resolveEnvWriteInput(context, source, command) {
|
|
126
|
+
if (source.kind === "file") return {
|
|
127
|
+
kind: "file",
|
|
128
|
+
filePath: source.filePath,
|
|
129
|
+
assignments: await readEnvFileAssignments(context.runtime.cwd, source.filePath, command)
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
kind: "single",
|
|
133
|
+
...parseKeyValuePositional(source.rawAssignment, command, context.runtime.env)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
110
136
|
async function runEnvList(context, flags) {
|
|
111
|
-
const
|
|
137
|
+
const explicit = resolveEnvScope(flags, {
|
|
112
138
|
requireExplicit: false,
|
|
113
139
|
command: "list"
|
|
114
|
-
})
|
|
115
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env list");
|
|
116
|
-
const resolved = await
|
|
117
|
-
|
|
140
|
+
});
|
|
141
|
+
const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env list");
|
|
142
|
+
const resolved = await resolveListScopeToApi(client, projectId, explicit ?? void 0, {
|
|
143
|
+
cwd: context.runtime.cwd,
|
|
118
144
|
signal: context.runtime.signal
|
|
119
145
|
});
|
|
120
|
-
const variables = await listVariables(client, projectId,
|
|
146
|
+
const variables = resolved.kind === "scoped" ? await listVariables(client, projectId, {
|
|
147
|
+
scope: resolved.addScope,
|
|
148
|
+
descriptor: resolved.descriptor,
|
|
149
|
+
apiTarget: resolved.apiTarget
|
|
150
|
+
}, context.runtime.signal) : await listOverviewVariables(client, projectId, context.runtime.signal);
|
|
121
151
|
return {
|
|
122
152
|
command: "project.env.list",
|
|
123
153
|
result: {
|
|
124
154
|
projectId,
|
|
155
|
+
verboseContext,
|
|
125
156
|
scope: resolved.descriptor,
|
|
157
|
+
target: resolved.target,
|
|
126
158
|
variables: variables.map((row) => toMetadata(row, resolved.descriptor))
|
|
127
159
|
},
|
|
128
160
|
warnings: [],
|
|
129
|
-
nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value ${formatScopeFlag(
|
|
161
|
+
nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value ${formatScopeFlag(resolved.addScope)}`] : []
|
|
130
162
|
};
|
|
131
163
|
}
|
|
132
164
|
async function runEnvRemove(context, key, flags) {
|
|
@@ -136,7 +168,7 @@ async function runEnvRemove(context, key, flags) {
|
|
|
136
168
|
command: "remove"
|
|
137
169
|
});
|
|
138
170
|
if (!scope) throw usageError("prisma-cli project env remove requires --role or --branch", "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env remove ${key} --role production`], "app");
|
|
139
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env remove");
|
|
171
|
+
const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env remove");
|
|
140
172
|
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
141
173
|
createBranchIfMissing: false,
|
|
142
174
|
signal: context.runtime.signal
|
|
@@ -160,6 +192,7 @@ async function runEnvRemove(context, key, flags) {
|
|
|
160
192
|
command: "project.env.remove",
|
|
161
193
|
result: {
|
|
162
194
|
projectId,
|
|
195
|
+
verboseContext,
|
|
163
196
|
scope: resolved.descriptor,
|
|
164
197
|
key
|
|
165
198
|
},
|
|
@@ -171,16 +204,25 @@ async function requireClientAndProject(context, explicitProject, commandName) {
|
|
|
171
204
|
const authState = await requireAuthenticatedAuthState(context);
|
|
172
205
|
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
173
206
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
174
|
-
|
|
207
|
+
const workspace = authState.workspace;
|
|
208
|
+
if (!workspace) throw workspaceRequiredError();
|
|
209
|
+
const targetResult = await resolveProjectTarget({
|
|
210
|
+
context,
|
|
211
|
+
workspace,
|
|
212
|
+
explicitProject,
|
|
213
|
+
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
|
|
214
|
+
commandName
|
|
215
|
+
});
|
|
216
|
+
if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
|
|
217
|
+
const target = targetResult.value;
|
|
175
218
|
return {
|
|
176
219
|
client,
|
|
177
|
-
projectId:
|
|
178
|
-
|
|
179
|
-
workspace
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
})).project.id
|
|
220
|
+
projectId: target.project.id,
|
|
221
|
+
verboseContext: {
|
|
222
|
+
workspace,
|
|
223
|
+
project: target.project,
|
|
224
|
+
resolution: target.resolution
|
|
225
|
+
}
|
|
184
226
|
};
|
|
185
227
|
}
|
|
186
228
|
async function resolveScopeToApi(client, projectId, scope, options) {
|
|
@@ -196,12 +238,12 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
196
238
|
}
|
|
197
239
|
};
|
|
198
240
|
const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName, options.signal) : await resolveExistingBranch(client, projectId, scope.branchName, options.signal);
|
|
199
|
-
if (branch.
|
|
241
|
+
if (branch.role === "production") throw new CliError({
|
|
200
242
|
code: "ENV_BRANCH_SCOPE_IS_PRODUCTION",
|
|
201
243
|
domain: "app",
|
|
202
|
-
summary: `Branch "${scope.branchName}" is the
|
|
244
|
+
summary: `Branch "${scope.branchName}" is the production branch`,
|
|
203
245
|
why: "Production variables are project-level only; branch overrides apply to preview branches.",
|
|
204
|
-
fix: "Use --role production for the
|
|
246
|
+
fix: "Use --role production for the production branch.",
|
|
205
247
|
exitCode: 1,
|
|
206
248
|
nextSteps: ["prisma-cli project env list --role production"]
|
|
207
249
|
});
|
|
@@ -218,6 +260,123 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
218
260
|
}
|
|
219
261
|
};
|
|
220
262
|
}
|
|
263
|
+
async function resolveListScopeToApi(client, projectId, explicit, options) {
|
|
264
|
+
if (explicit) {
|
|
265
|
+
const resolved = await resolveScopeToApi(client, projectId, explicit, {
|
|
266
|
+
createBranchIfMissing: false,
|
|
267
|
+
signal: options.signal
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
kind: "scoped",
|
|
271
|
+
descriptor: resolved.descriptor,
|
|
272
|
+
target: targetFromExplicitScope(resolved.descriptor),
|
|
273
|
+
apiTarget: resolved.apiTarget,
|
|
274
|
+
addScope: resolved.scope
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const gitBranch = await readLocalGitBranch(options.cwd, options.signal);
|
|
278
|
+
if (gitBranch) {
|
|
279
|
+
const branch = (await listBranchesByName(client, projectId, gitBranch, options.signal))[0];
|
|
280
|
+
if (!branch) return {
|
|
281
|
+
kind: "scoped",
|
|
282
|
+
descriptor: {
|
|
283
|
+
kind: "role",
|
|
284
|
+
role: "preview"
|
|
285
|
+
},
|
|
286
|
+
target: {
|
|
287
|
+
source: "local-git",
|
|
288
|
+
branchName: gitBranch,
|
|
289
|
+
branchExists: false,
|
|
290
|
+
envMap: "preview"
|
|
291
|
+
},
|
|
292
|
+
apiTarget: {
|
|
293
|
+
class: "preview",
|
|
294
|
+
branchId: null
|
|
295
|
+
},
|
|
296
|
+
addScope: {
|
|
297
|
+
kind: "branch",
|
|
298
|
+
branchName: gitBranch
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
if (branch.role === "production") return {
|
|
302
|
+
kind: "scoped",
|
|
303
|
+
descriptor: {
|
|
304
|
+
kind: "role",
|
|
305
|
+
role: "production"
|
|
306
|
+
},
|
|
307
|
+
target: {
|
|
308
|
+
source: "local-git",
|
|
309
|
+
branchName: branch.gitName,
|
|
310
|
+
branchId: branch.id,
|
|
311
|
+
branchRole: branch.role,
|
|
312
|
+
branchExists: true,
|
|
313
|
+
envMap: "production"
|
|
314
|
+
},
|
|
315
|
+
apiTarget: {
|
|
316
|
+
class: "production",
|
|
317
|
+
branchId: null
|
|
318
|
+
},
|
|
319
|
+
addScope: {
|
|
320
|
+
kind: "role",
|
|
321
|
+
role: "production"
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
kind: "scoped",
|
|
326
|
+
descriptor: {
|
|
327
|
+
kind: "branch",
|
|
328
|
+
branchName: branch.gitName,
|
|
329
|
+
branchId: branch.id
|
|
330
|
+
},
|
|
331
|
+
target: {
|
|
332
|
+
source: "local-git",
|
|
333
|
+
branchName: branch.gitName,
|
|
334
|
+
branchId: branch.id,
|
|
335
|
+
branchRole: branch.role,
|
|
336
|
+
branchExists: true,
|
|
337
|
+
envMap: "preview"
|
|
338
|
+
},
|
|
339
|
+
apiTarget: {
|
|
340
|
+
class: "preview",
|
|
341
|
+
branchId: branch.id
|
|
342
|
+
},
|
|
343
|
+
addScope: {
|
|
344
|
+
kind: "branch",
|
|
345
|
+
branchName: branch.gitName
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
kind: "overview",
|
|
351
|
+
descriptor: { kind: "overview" },
|
|
352
|
+
target: {
|
|
353
|
+
source: "overview",
|
|
354
|
+
envMap: "overview"
|
|
355
|
+
},
|
|
356
|
+
addScope: {
|
|
357
|
+
kind: "role",
|
|
358
|
+
role: "preview"
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function targetFromExplicitScope(scope) {
|
|
363
|
+
if (scope.kind === "branch") return {
|
|
364
|
+
source: "explicit",
|
|
365
|
+
branchName: scope.branchName,
|
|
366
|
+
branchId: scope.branchId,
|
|
367
|
+
branchRole: "preview",
|
|
368
|
+
branchExists: true,
|
|
369
|
+
envMap: "preview"
|
|
370
|
+
};
|
|
371
|
+
if (scope.kind === "role") return {
|
|
372
|
+
source: "explicit",
|
|
373
|
+
envMap: scope.role
|
|
374
|
+
};
|
|
375
|
+
return {
|
|
376
|
+
source: "overview",
|
|
377
|
+
envMap: "overview"
|
|
378
|
+
};
|
|
379
|
+
}
|
|
221
380
|
function formatScopeFlag(scope) {
|
|
222
381
|
if (scope.kind === "role") return `--role ${scope.role}`;
|
|
223
382
|
return `--branch ${scope.branchName}`;
|
|
@@ -293,47 +452,45 @@ async function projectHasDefaultBranch(client, projectId, signal) {
|
|
|
293
452
|
cursor = result.data.pagination.nextCursor;
|
|
294
453
|
}
|
|
295
454
|
}
|
|
296
|
-
async function
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
455
|
+
async function listVariables(client, projectId, resolved, signal) {
|
|
456
|
+
return materializeEffectiveRows(await collectEnvironmentVariables(client, projectId, signal, {
|
|
457
|
+
className: resolved.apiTarget.class,
|
|
458
|
+
filter: (row) => rowMatchesScope(row, resolved)
|
|
459
|
+
}), resolved);
|
|
460
|
+
}
|
|
461
|
+
async function listOverviewVariables(client, projectId, signal) {
|
|
462
|
+
return (await collectEnvironmentVariables(client, projectId, signal, { filter: (row) => row.branchId === null && (row.class === "production" || row.class === "preview") })).sort((left, right) => {
|
|
463
|
+
const roleOrder = roleSortOrder(left.class) - roleSortOrder(right.class);
|
|
464
|
+
return roleOrder !== 0 ? roleOrder : left.key.localeCompare(right.key);
|
|
304
465
|
});
|
|
305
|
-
if (error || !data) throw apiCallError(`Failed to look up ${key}`, response, error);
|
|
306
|
-
return data.data.filter((row) => rowMatchesExactScope(row, resolved))[0] ?? null;
|
|
307
466
|
}
|
|
308
|
-
async function
|
|
467
|
+
async function collectEnvironmentVariables(client, projectId, signal, options) {
|
|
309
468
|
const collected = [];
|
|
310
469
|
let cursor;
|
|
311
470
|
while (true) {
|
|
312
|
-
const query = {
|
|
313
|
-
|
|
314
|
-
class: resolved.apiTarget.class
|
|
315
|
-
};
|
|
471
|
+
const query = { projectId };
|
|
472
|
+
if (options.className !== void 0) query.class = options.className;
|
|
316
473
|
if (cursor !== void 0) query.cursor = cursor;
|
|
317
474
|
const result = await client.GET("/v1/environment-variables", {
|
|
318
475
|
params: { query },
|
|
319
476
|
signal
|
|
320
477
|
});
|
|
321
478
|
if (result.error || !result.data) throw apiCallError(`Failed to list environment variables`, result.response, result.error);
|
|
322
|
-
const page = result.data.data.filter(
|
|
479
|
+
const page = result.data.data.filter(options.filter);
|
|
323
480
|
collected.push(...page);
|
|
324
481
|
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
|
|
325
482
|
cursor = result.data.pagination.nextCursor;
|
|
326
483
|
}
|
|
327
|
-
return
|
|
484
|
+
return collected;
|
|
485
|
+
}
|
|
486
|
+
function roleSortOrder(role) {
|
|
487
|
+
return role === "production" ? 0 : 1;
|
|
328
488
|
}
|
|
329
489
|
function rowMatchesScope(row, resolved) {
|
|
330
490
|
if (row.class !== resolved.apiTarget.class) return false;
|
|
331
491
|
if (resolved.apiTarget.branchId === null) return row.branchId === null;
|
|
332
492
|
return row.branchId === null || row.branchId === resolved.apiTarget.branchId;
|
|
333
493
|
}
|
|
334
|
-
function rowMatchesExactScope(row, resolved) {
|
|
335
|
-
return row.class === resolved.apiTarget.class && row.branchId === resolved.apiTarget.branchId;
|
|
336
|
-
}
|
|
337
494
|
function materializeEffectiveRows(rows, resolved) {
|
|
338
495
|
if (resolved.apiTarget.branchId === null) return rows;
|
|
339
496
|
const byKey = /* @__PURE__ */ new Map();
|
|
@@ -341,39 +498,5 @@ function materializeEffectiveRows(rows, resolved) {
|
|
|
341
498
|
for (const row of rows) if (row.branchId === resolved.apiTarget.branchId) byKey.set(row.key, row);
|
|
342
499
|
return [...byKey.values()].sort((left, right) => left.key.localeCompare(right.key));
|
|
343
500
|
}
|
|
344
|
-
function toMetadata(row, requestedScope) {
|
|
345
|
-
const rowScope = row.branchId === null ? {
|
|
346
|
-
kind: "role",
|
|
347
|
-
role: row.class
|
|
348
|
-
} : requestedScope;
|
|
349
|
-
return {
|
|
350
|
-
id: row.id,
|
|
351
|
-
key: row.key,
|
|
352
|
-
scope: rowScope,
|
|
353
|
-
source: formatDescriptorLabel(rowScope),
|
|
354
|
-
isManagedBySystem: row.isManagedBySystem,
|
|
355
|
-
updatedAt: row.updatedAt
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
function formatDescriptorLabel(scope) {
|
|
359
|
-
if (scope.kind === "role") return scope.role ?? "unknown";
|
|
360
|
-
return `branch:${scope.branchName ?? scope.branchId ?? "unknown"}`;
|
|
361
|
-
}
|
|
362
|
-
function apiCallError(summary, response, error) {
|
|
363
|
-
const status = response?.status ?? 0;
|
|
364
|
-
const apiCode = error?.error?.code;
|
|
365
|
-
const apiMessage = error?.error?.message;
|
|
366
|
-
const apiHint = error?.error?.hint;
|
|
367
|
-
if (status === 401 || status === 403) return authRequiredError(["prisma auth login"]);
|
|
368
|
-
return new CliError({
|
|
369
|
-
code: apiCode ?? "ENV_API_ERROR",
|
|
370
|
-
domain: "app",
|
|
371
|
-
summary,
|
|
372
|
-
why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
|
|
373
|
-
fix: apiHint ?? "Re-run with --trace for the underlying API response details.",
|
|
374
|
-
exitCode: 1,
|
|
375
|
-
nextSteps: []
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
501
|
//#endregion
|
|
379
502
|
export { runEnvAdd, runEnvList, runEnvRemove, runEnvUpdate };
|