@prisma/cli 3.0.0-beta.2 → 3.0.0-beta.20
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 +5 -16
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +26 -7
- package/dist/adapters/mock-api.js +81 -2
- package/dist/adapters/token-storage.js +344 -25
- package/dist/cli.js +18 -3
- package/dist/cli2.js +12 -4
- package/dist/commands/agent/index.js +60 -0
- package/dist/commands/app/index.js +90 -61
- package/dist/commands/auth/index.js +55 -2
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/build/index.js +29 -0
- package/dist/commands/database/index.js +159 -0
- package/dist/commands/env.js +8 -4
- package/dist/commands/git/index.js +1 -1
- package/dist/commands/project/index.js +1 -1
- package/dist/controllers/agent.js +228 -0
- package/dist/controllers/app-env-api.js +54 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +286 -131
- package/dist/controllers/app.js +849 -309
- package/dist/controllers/auth.js +255 -11
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/build.js +88 -0
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +156 -87
- package/dist/controllers/select-prompt-port.js +1 -0
- package/dist/lib/agent/cli-command.js +17 -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/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +220 -104
- 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 +82 -0
- package/dist/lib/app/bun-project.js +15 -9
- 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 +8 -49
- 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 +38 -23
- package/dist/lib/auth/guard.js +6 -2
- package/dist/lib/auth/login.js +117 -15
- package/dist/lib/database/provider.js +167 -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 +2 -1
- package/dist/lib/project/local-pin.js +183 -34
- package/dist/lib/project/resolution.js +206 -50
- package/dist/lib/project/setup.js +64 -18
- 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 +187 -26
- package/dist/presenters/auth.js +99 -2
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +44 -26
- 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 +243 -15
- package/dist/shell/command-runner.js +60 -21
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +61 -1
- package/dist/shell/help.js +31 -20
- package/dist/shell/output.js +10 -1
- package/dist/shell/prompt.js +7 -3
- package/dist/shell/runtime.js +10 -6
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +247 -0
- 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/package.json +27 -10
- package/dist/lib/app/preview-build.js +0 -284
- package/dist/lib/app/preview-interaction.js +0 -5
|
@@ -1,39 +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
|
|
22
|
-
const
|
|
23
|
-
|
|
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");
|
|
21
|
+
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
22
|
+
createBranchIfMissing: true,
|
|
23
|
+
signal: context.runtime.signal
|
|
24
|
+
});
|
|
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({
|
|
24
27
|
code: "ENV_VARIABLE_ALREADY_EXISTS",
|
|
25
28
|
domain: "app",
|
|
26
|
-
summary: `Variable "${key}" already exists in ${formatScopeLabel(scope)}`,
|
|
29
|
+
summary: `Variable "${input.key}" already exists in ${formatScopeLabel(scope)}`,
|
|
27
30
|
why: "A variable with this key already exists in the targeted scope.",
|
|
28
31
|
fix: "Use `prisma-cli project env update` to change an existing variable's value.",
|
|
29
32
|
exitCode: 1,
|
|
30
|
-
nextSteps: [`prisma-cli project env update ${key}=<new-value> ${formatScopeFlag(scope)}`]
|
|
33
|
+
nextSteps: [`prisma-cli project env update ${input.key}=<new-value> ${formatScopeFlag(scope)}`]
|
|
31
34
|
});
|
|
32
|
-
const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(client, projectId, key, {
|
|
33
|
-
scope: {
|
|
34
|
-
kind: "role",
|
|
35
|
-
role: "preview"
|
|
36
|
-
},
|
|
35
|
+
const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(client, projectId, input.key, {
|
|
37
36
|
descriptor: {
|
|
38
37
|
kind: "role",
|
|
39
38
|
role: "preview"
|
|
@@ -42,19 +41,23 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
42
41
|
class: "preview",
|
|
43
42
|
branchId: null
|
|
44
43
|
}
|
|
45
|
-
}) ? [`Variable "${key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
46
|
-
const { data, error, response } = await client.POST("/v1/environment-variables", {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
}, context.runtime.signal) ? [`Variable "${input.key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
45
|
+
const { data, error, response } = await client.POST("/v1/environment-variables", {
|
|
46
|
+
body: {
|
|
47
|
+
projectId,
|
|
48
|
+
class: resolved.apiTarget.class,
|
|
49
|
+
...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
|
|
50
|
+
key: input.key,
|
|
51
|
+
value: input.value
|
|
52
|
+
},
|
|
53
|
+
signal: context.runtime.signal
|
|
54
|
+
});
|
|
55
|
+
if (error || !data) throw apiCallError(`Failed to add ${input.key}`, response, error);
|
|
54
56
|
return {
|
|
55
57
|
command: "project.env.add",
|
|
56
58
|
result: {
|
|
57
59
|
projectId,
|
|
60
|
+
verboseContext,
|
|
58
61
|
scope: resolved.descriptor,
|
|
59
62
|
variable: toMetadata(data.data, resolved.descriptor)
|
|
60
63
|
},
|
|
@@ -63,33 +66,40 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
68
|
async function runEnvUpdate(context, rawAssignment, flags) {
|
|
66
|
-
const
|
|
69
|
+
const source = resolveEnvWriteSource(rawAssignment, flags.filePath, "update");
|
|
67
70
|
const scope = resolveEnvScope(flags, {
|
|
68
71
|
requireExplicit: true,
|
|
69
72
|
command: "update"
|
|
70
73
|
});
|
|
71
|
-
if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
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");
|
|
77
|
+
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
78
|
+
createBranchIfMissing: false,
|
|
79
|
+
signal: context.runtime.signal
|
|
80
|
+
});
|
|
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);
|
|
75
83
|
if (!existing) throw new CliError({
|
|
76
84
|
code: "ENV_VARIABLE_NOT_FOUND",
|
|
77
85
|
domain: "app",
|
|
78
|
-
summary: `Variable "${key}" not found in ${formatScopeLabel(scope)}`,
|
|
86
|
+
summary: `Variable "${input.key}" not found in ${formatScopeLabel(scope)}`,
|
|
79
87
|
why: "No variable with this key exists in the targeted scope.",
|
|
80
88
|
fix: "Use `prisma-cli project env add` to create a new variable.",
|
|
81
89
|
exitCode: 1,
|
|
82
|
-
nextSteps: [`prisma-cli project env add ${key}=<value> ${formatScopeFlag(scope)}`]
|
|
90
|
+
nextSteps: [`prisma-cli project env add ${input.key}=<value> ${formatScopeFlag(scope)}`]
|
|
83
91
|
});
|
|
84
92
|
const { data, error, response } = await client.PATCH("/v1/environment-variables/{envVarId}", {
|
|
85
93
|
params: { path: { envVarId: existing.id } },
|
|
86
|
-
body: { value }
|
|
94
|
+
body: { value: input.value },
|
|
95
|
+
signal: context.runtime.signal
|
|
87
96
|
});
|
|
88
|
-
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);
|
|
89
98
|
return {
|
|
90
99
|
command: "project.env.update",
|
|
91
100
|
result: {
|
|
92
101
|
projectId,
|
|
102
|
+
verboseContext,
|
|
93
103
|
scope: resolved.descriptor,
|
|
94
104
|
variable: toMetadata(data.data, resolved.descriptor)
|
|
95
105
|
},
|
|
@@ -97,23 +107,58 @@ async function runEnvUpdate(context, rawAssignment, flags) {
|
|
|
97
107
|
nextSteps: []
|
|
98
108
|
};
|
|
99
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
|
+
}
|
|
100
136
|
async function runEnvList(context, flags) {
|
|
101
|
-
const
|
|
137
|
+
const explicit = resolveEnvScope(flags, {
|
|
102
138
|
requireExplicit: false,
|
|
103
139
|
command: "list"
|
|
104
|
-
})
|
|
105
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env list");
|
|
106
|
-
const resolved = await
|
|
107
|
-
|
|
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,
|
|
144
|
+
signal: context.runtime.signal
|
|
145
|
+
});
|
|
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);
|
|
108
151
|
return {
|
|
109
152
|
command: "project.env.list",
|
|
110
153
|
result: {
|
|
111
154
|
projectId,
|
|
155
|
+
verboseContext,
|
|
112
156
|
scope: resolved.descriptor,
|
|
157
|
+
target: resolved.target,
|
|
113
158
|
variables: variables.map((row) => toMetadata(row, resolved.descriptor))
|
|
114
159
|
},
|
|
115
160
|
warnings: [],
|
|
116
|
-
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)}`] : []
|
|
117
162
|
};
|
|
118
163
|
}
|
|
119
164
|
async function runEnvRemove(context, key, flags) {
|
|
@@ -123,9 +168,12 @@ async function runEnvRemove(context, key, flags) {
|
|
|
123
168
|
command: "remove"
|
|
124
169
|
});
|
|
125
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");
|
|
126
|
-
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env remove");
|
|
127
|
-
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
128
|
-
|
|
171
|
+
const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env remove");
|
|
172
|
+
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
173
|
+
createBranchIfMissing: false,
|
|
174
|
+
signal: context.runtime.signal
|
|
175
|
+
});
|
|
176
|
+
const existing = await findVariableByNaturalKey(client, projectId, key, resolved, context.runtime.signal);
|
|
129
177
|
if (!existing) throw new CliError({
|
|
130
178
|
code: "ENV_VARIABLE_NOT_FOUND",
|
|
131
179
|
domain: "app",
|
|
@@ -135,12 +183,16 @@ async function runEnvRemove(context, key, flags) {
|
|
|
135
183
|
exitCode: 1,
|
|
136
184
|
nextSteps: [`prisma-cli project env list ${formatScopeFlag(scope)}`]
|
|
137
185
|
});
|
|
138
|
-
const { error, response } = await client.DELETE("/v1/environment-variables/{envVarId}", {
|
|
186
|
+
const { error, response } = await client.DELETE("/v1/environment-variables/{envVarId}", {
|
|
187
|
+
params: { path: { envVarId: existing.id } },
|
|
188
|
+
signal: context.runtime.signal
|
|
189
|
+
});
|
|
139
190
|
if (error) throw apiCallError(`Failed to remove ${key}`, response, error);
|
|
140
191
|
return {
|
|
141
192
|
command: "project.env.remove",
|
|
142
193
|
result: {
|
|
143
194
|
projectId,
|
|
195
|
+
verboseContext,
|
|
144
196
|
scope: resolved.descriptor,
|
|
145
197
|
key
|
|
146
198
|
},
|
|
@@ -150,18 +202,27 @@ async function runEnvRemove(context, key, flags) {
|
|
|
150
202
|
}
|
|
151
203
|
async function requireClientAndProject(context, explicitProject, commandName) {
|
|
152
204
|
const authState = await requireAuthenticatedAuthState(context);
|
|
153
|
-
const client = await requireComputeAuth(context.runtime.env);
|
|
205
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
154
206
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
155
|
-
|
|
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;
|
|
156
218
|
return {
|
|
157
219
|
client,
|
|
158
|
-
projectId:
|
|
159
|
-
|
|
160
|
-
workspace
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
})).project.id
|
|
220
|
+
projectId: target.project.id,
|
|
221
|
+
verboseContext: {
|
|
222
|
+
workspace,
|
|
223
|
+
project: target.project,
|
|
224
|
+
resolution: target.resolution
|
|
225
|
+
}
|
|
165
226
|
};
|
|
166
227
|
}
|
|
167
228
|
async function resolveScopeToApi(client, projectId, scope, options) {
|
|
@@ -176,13 +237,13 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
176
237
|
branchId: null
|
|
177
238
|
}
|
|
178
239
|
};
|
|
179
|
-
const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName) : await resolveExistingBranch(client, projectId, scope.branchName);
|
|
180
|
-
if (branch.
|
|
240
|
+
const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName, options.signal) : await resolveExistingBranch(client, projectId, scope.branchName, options.signal);
|
|
241
|
+
if (branch.role === "production") throw new CliError({
|
|
181
242
|
code: "ENV_BRANCH_SCOPE_IS_PRODUCTION",
|
|
182
243
|
domain: "app",
|
|
183
|
-
summary: `Branch "${scope.branchName}" is the
|
|
244
|
+
summary: `Branch "${scope.branchName}" is the production branch`,
|
|
184
245
|
why: "Production variables are project-level only; branch overrides apply to preview branches.",
|
|
185
|
-
fix: "Use --role production for the
|
|
246
|
+
fix: "Use --role production for the production branch.",
|
|
186
247
|
exitCode: 1,
|
|
187
248
|
nextSteps: ["prisma-cli project env list --role production"]
|
|
188
249
|
});
|
|
@@ -199,20 +260,140 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
199
260
|
}
|
|
200
261
|
};
|
|
201
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
|
+
}
|
|
202
380
|
function formatScopeFlag(scope) {
|
|
203
381
|
if (scope.kind === "role") return `--role ${scope.role}`;
|
|
204
382
|
return `--branch ${scope.branchName}`;
|
|
205
383
|
}
|
|
206
|
-
async function listBranchesByName(client, projectId, branchName) {
|
|
207
|
-
const { data, error, response } = await client.GET("/v1/projects/{projectId}/branches", {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
384
|
+
async function listBranchesByName(client, projectId, branchName, signal) {
|
|
385
|
+
const { data, error, response } = await client.GET("/v1/projects/{projectId}/branches", {
|
|
386
|
+
params: {
|
|
387
|
+
path: { projectId },
|
|
388
|
+
query: { gitName: branchName }
|
|
389
|
+
},
|
|
390
|
+
signal
|
|
391
|
+
});
|
|
211
392
|
if (error || !data) throw apiCallError(`Failed to resolve branch "${branchName}"`, response, error);
|
|
212
393
|
return data.data;
|
|
213
394
|
}
|
|
214
|
-
async function resolveExistingBranch(client, projectId, branchName) {
|
|
215
|
-
const branch = (await listBranchesByName(client, projectId, branchName))[0];
|
|
395
|
+
async function resolveExistingBranch(client, projectId, branchName, signal) {
|
|
396
|
+
const branch = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
216
397
|
if (!branch) throw new CliError({
|
|
217
398
|
code: "ENV_BRANCH_NOT_FOUND",
|
|
218
399
|
domain: "app",
|
|
@@ -224,10 +405,10 @@ async function resolveExistingBranch(client, projectId, branchName) {
|
|
|
224
405
|
});
|
|
225
406
|
return branch;
|
|
226
407
|
}
|
|
227
|
-
async function resolveOrCreateBranch(client, projectId, branchName) {
|
|
228
|
-
const existing = (await listBranchesByName(client, projectId, branchName))[0];
|
|
408
|
+
async function resolveOrCreateBranch(client, projectId, branchName, signal) {
|
|
409
|
+
const existing = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
229
410
|
if (existing) return existing;
|
|
230
|
-
if (!await projectHasDefaultBranch(client, projectId)) throw new CliError({
|
|
411
|
+
if (!await projectHasDefaultBranch(client, projectId, signal)) throw new CliError({
|
|
231
412
|
code: "ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH",
|
|
232
413
|
domain: "app",
|
|
233
414
|
summary: `Cannot create branch "${branchName}" from project env`,
|
|
@@ -241,67 +422,75 @@ async function resolveOrCreateBranch(client, projectId, branchName) {
|
|
|
241
422
|
body: {
|
|
242
423
|
gitName: branchName,
|
|
243
424
|
isDefault: false
|
|
244
|
-
}
|
|
425
|
+
},
|
|
426
|
+
signal
|
|
245
427
|
});
|
|
246
428
|
if (error || !data) {
|
|
247
429
|
if (response?.status === 409) {
|
|
248
|
-
const raced = (await listBranchesByName(client, projectId, branchName))[0];
|
|
430
|
+
const raced = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
249
431
|
if (raced) return raced;
|
|
250
432
|
}
|
|
251
433
|
throw apiCallError(`Failed to create branch "${branchName}"`, response, error);
|
|
252
434
|
}
|
|
253
435
|
return data.data;
|
|
254
436
|
}
|
|
255
|
-
async function projectHasDefaultBranch(client, projectId) {
|
|
437
|
+
async function projectHasDefaultBranch(client, projectId, signal) {
|
|
256
438
|
let cursor;
|
|
257
439
|
while (true) {
|
|
258
440
|
const query = {};
|
|
259
441
|
if (cursor !== void 0) query.cursor = cursor;
|
|
260
|
-
const result = await client.GET("/v1/projects/{projectId}/branches", {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
442
|
+
const result = await client.GET("/v1/projects/{projectId}/branches", {
|
|
443
|
+
params: {
|
|
444
|
+
path: { projectId },
|
|
445
|
+
query
|
|
446
|
+
},
|
|
447
|
+
signal
|
|
448
|
+
});
|
|
264
449
|
if (result.error || !result.data) throw apiCallError("Failed to check project default branch", result.response, result.error);
|
|
265
450
|
if (result.data.data.some((branch) => branch.isDefault)) return true;
|
|
266
451
|
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) return false;
|
|
267
452
|
cursor = result.data.pagination.nextCursor;
|
|
268
453
|
}
|
|
269
454
|
}
|
|
270
|
-
async function
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
return
|
|
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);
|
|
465
|
+
});
|
|
278
466
|
}
|
|
279
|
-
async function
|
|
467
|
+
async function collectEnvironmentVariables(client, projectId, signal, options) {
|
|
280
468
|
const collected = [];
|
|
281
469
|
let cursor;
|
|
282
470
|
while (true) {
|
|
283
|
-
const query = {
|
|
284
|
-
|
|
285
|
-
class: resolved.apiTarget.class
|
|
286
|
-
};
|
|
471
|
+
const query = { projectId };
|
|
472
|
+
if (options.className !== void 0) query.class = options.className;
|
|
287
473
|
if (cursor !== void 0) query.cursor = cursor;
|
|
288
|
-
const result = await client.GET("/v1/environment-variables", {
|
|
474
|
+
const result = await client.GET("/v1/environment-variables", {
|
|
475
|
+
params: { query },
|
|
476
|
+
signal
|
|
477
|
+
});
|
|
289
478
|
if (result.error || !result.data) throw apiCallError(`Failed to list environment variables`, result.response, result.error);
|
|
290
|
-
const page = result.data.data.filter(
|
|
479
|
+
const page = result.data.data.filter(options.filter);
|
|
291
480
|
collected.push(...page);
|
|
292
481
|
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
|
|
293
482
|
cursor = result.data.pagination.nextCursor;
|
|
294
483
|
}
|
|
295
|
-
return
|
|
484
|
+
return collected;
|
|
485
|
+
}
|
|
486
|
+
function roleSortOrder(role) {
|
|
487
|
+
return role === "production" ? 0 : 1;
|
|
296
488
|
}
|
|
297
489
|
function rowMatchesScope(row, resolved) {
|
|
298
490
|
if (row.class !== resolved.apiTarget.class) return false;
|
|
299
491
|
if (resolved.apiTarget.branchId === null) return row.branchId === null;
|
|
300
492
|
return row.branchId === null || row.branchId === resolved.apiTarget.branchId;
|
|
301
493
|
}
|
|
302
|
-
function rowMatchesExactScope(row, resolved) {
|
|
303
|
-
return row.class === resolved.apiTarget.class && row.branchId === resolved.apiTarget.branchId;
|
|
304
|
-
}
|
|
305
494
|
function materializeEffectiveRows(rows, resolved) {
|
|
306
495
|
if (resolved.apiTarget.branchId === null) return rows;
|
|
307
496
|
const byKey = /* @__PURE__ */ new Map();
|
|
@@ -309,39 +498,5 @@ function materializeEffectiveRows(rows, resolved) {
|
|
|
309
498
|
for (const row of rows) if (row.branchId === resolved.apiTarget.branchId) byKey.set(row.key, row);
|
|
310
499
|
return [...byKey.values()].sort((left, right) => left.key.localeCompare(right.key));
|
|
311
500
|
}
|
|
312
|
-
function toMetadata(row, requestedScope) {
|
|
313
|
-
const rowScope = row.branchId === null ? {
|
|
314
|
-
kind: "role",
|
|
315
|
-
role: row.class
|
|
316
|
-
} : requestedScope;
|
|
317
|
-
return {
|
|
318
|
-
id: row.id,
|
|
319
|
-
key: row.key,
|
|
320
|
-
scope: rowScope,
|
|
321
|
-
source: formatDescriptorLabel(rowScope),
|
|
322
|
-
isManagedBySystem: row.isManagedBySystem,
|
|
323
|
-
updatedAt: row.updatedAt
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
function formatDescriptorLabel(scope) {
|
|
327
|
-
if (scope.kind === "role") return scope.role ?? "unknown";
|
|
328
|
-
return `branch:${scope.branchName ?? scope.branchId ?? "unknown"}`;
|
|
329
|
-
}
|
|
330
|
-
function apiCallError(summary, response, error) {
|
|
331
|
-
const status = response?.status ?? 0;
|
|
332
|
-
const apiCode = error?.error?.code;
|
|
333
|
-
const apiMessage = error?.error?.message;
|
|
334
|
-
const apiHint = error?.error?.hint;
|
|
335
|
-
if (status === 401 || status === 403) return authRequiredError(["prisma auth login"]);
|
|
336
|
-
return new CliError({
|
|
337
|
-
code: apiCode ?? "ENV_API_ERROR",
|
|
338
|
-
domain: "app",
|
|
339
|
-
summary,
|
|
340
|
-
why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
|
|
341
|
-
fix: apiHint ?? "Re-run with --trace for the underlying API response details.",
|
|
342
|
-
exitCode: 1,
|
|
343
|
-
nextSteps: []
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
501
|
//#endregion
|
|
347
502
|
export { runEnvAdd, runEnvList, runEnvRemove, runEnvUpdate };
|