@prisma/cli 3.0.0-dev.75.1 → 3.0.0-dev.77.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/app.js +5 -3
- package/dist/controllers/project.js +14 -7
- package/dist/lib/project/local-pin.js +106 -26
- package/dist/lib/project/setup.js +58 -15
- package/package.json +1 -1
package/dist/controllers/app.js
CHANGED
|
@@ -14,7 +14,7 @@ import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../l
|
|
|
14
14
|
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
15
15
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
16
16
|
import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
17
|
-
import { bindProjectToDirectory, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
17
|
+
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
18
18
|
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
19
19
|
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
20
20
|
import { resolveOrCreatePreviewBuildSettings } from "../lib/app/preview-build-settings.js";
|
|
@@ -131,8 +131,10 @@ async function runAppDeploy(context, appName, options) {
|
|
|
131
131
|
let localPinResult;
|
|
132
132
|
if (target.localPinAction) {
|
|
133
133
|
const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
|
|
135
|
+
const projectSetup = setupResult.value;
|
|
136
|
+
localPinResult = projectSetup.localPin;
|
|
137
|
+
maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
|
|
136
138
|
}
|
|
137
139
|
let framework = await resolveDeployFramework(context, {
|
|
138
140
|
requestedFramework: options?.framework,
|
|
@@ -5,7 +5,7 @@ import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
|
5
5
|
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
6
6
|
import { readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
7
7
|
import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
8
|
-
import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
8
|
+
import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
9
9
|
import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
|
|
10
10
|
import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
|
|
11
11
|
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
|
|
@@ -116,12 +116,14 @@ async function runProjectCreate(context, projectName) {
|
|
|
116
116
|
fallbackFix: "Retry the command, or choose an existing Project with prisma-cli project link <id-or-name>."
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
|
+
const bindResult = await bindProjectToDirectory(context, workspace, {
|
|
120
|
+
id: created.id,
|
|
121
|
+
name: created.name
|
|
122
|
+
}, "created");
|
|
123
|
+
if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
|
|
119
124
|
return {
|
|
120
125
|
command: "project.create",
|
|
121
|
-
result:
|
|
122
|
-
id: created.id,
|
|
123
|
-
name: created.name
|
|
124
|
-
}, "created"),
|
|
126
|
+
result: bindResult.value,
|
|
125
127
|
warnings: [],
|
|
126
128
|
nextSteps: ["prisma-cli app deploy"]
|
|
127
129
|
};
|
|
@@ -138,7 +140,7 @@ async function runProjectLink(context, projectRef) {
|
|
|
138
140
|
projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
|
|
139
141
|
} else projects = listFixtureWorkspaceProjects(context, workspace);
|
|
140
142
|
let result;
|
|
141
|
-
if (projectRef?.trim()) result = await
|
|
143
|
+
if (projectRef?.trim()) result = await requireProjectDirectoryBinding(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
|
|
142
144
|
else if (canPrompt(context) && !context.flags.yes) result = await resolveInteractiveProjectLinkSetup(context, workspace, projects, provider);
|
|
143
145
|
else throw await projectLinkTargetRequiredError(context, projects);
|
|
144
146
|
return {
|
|
@@ -162,7 +164,12 @@ async function resolveInteractiveProjectLinkSetup(context, workspace, projects,
|
|
|
162
164
|
nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli project create <name>"]
|
|
163
165
|
}
|
|
164
166
|
});
|
|
165
|
-
return
|
|
167
|
+
return requireProjectDirectoryBinding(context, workspace, setup.project, setup.action);
|
|
168
|
+
}
|
|
169
|
+
async function requireProjectDirectoryBinding(context, workspace, project, action) {
|
|
170
|
+
const bindResult = await bindProjectToDirectory(context, workspace, project, action);
|
|
171
|
+
if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
|
|
172
|
+
return bindResult.value;
|
|
166
173
|
}
|
|
167
174
|
async function createProjectForLinkSetup(provider, projectName, workspace, signal) {
|
|
168
175
|
const created = await provider.createProject({
|
|
@@ -29,6 +29,53 @@ var LocalResolutionPinReadAbortedError = class extends TaggedError("LocalResolut
|
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
+
var LocalResolutionPinSerializationError = class extends TaggedError("LocalResolutionPinSerializationError")() {
|
|
33
|
+
constructor(cause) {
|
|
34
|
+
super({
|
|
35
|
+
message: `Could not serialize ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
|
|
36
|
+
cause,
|
|
37
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var LocalResolutionPinWriteAbortedError = class extends TaggedError("LocalResolutionPinWriteAbortedError")() {
|
|
42
|
+
constructor(cause) {
|
|
43
|
+
super({
|
|
44
|
+
message: `Writing ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} was aborted.`,
|
|
45
|
+
cause,
|
|
46
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var LocalResolutionPinWriteFailedError = class extends TaggedError("LocalResolutionPinWriteFailedError")() {
|
|
51
|
+
constructor(operation, cause) {
|
|
52
|
+
super({
|
|
53
|
+
message: `Could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
|
|
54
|
+
cause,
|
|
55
|
+
operation,
|
|
56
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var LocalResolutionPinGitignoreUpdateAbortedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateAbortedError")() {
|
|
61
|
+
constructor(cause) {
|
|
62
|
+
super({
|
|
63
|
+
message: "Updating .gitignore for the local Project binding was aborted.",
|
|
64
|
+
cause,
|
|
65
|
+
gitignorePath: ".gitignore"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var LocalResolutionPinGitignoreUpdateFailedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateFailedError")() {
|
|
70
|
+
constructor(operation, cause) {
|
|
71
|
+
super({
|
|
72
|
+
message: "Could not update .gitignore for the local Project binding.",
|
|
73
|
+
cause,
|
|
74
|
+
operation,
|
|
75
|
+
gitignorePath: ".gitignore"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
32
79
|
async function readLocalResolutionPin(cwd, signal) {
|
|
33
80
|
return Result.gen(async function* () {
|
|
34
81
|
yield* ensureLocalResolutionPinReadNotAborted(signal);
|
|
@@ -72,41 +119,74 @@ function parseLocalResolutionPin(raw) {
|
|
|
72
119
|
});
|
|
73
120
|
}
|
|
74
121
|
async function writeLocalResolutionPin(cwd, pin, signal) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
122
|
+
return Result.gen(async function* () {
|
|
123
|
+
const prismaDir = path.join(cwd, ".prisma");
|
|
124
|
+
yield* ensureLocalResolutionPinWriteNotAborted(signal);
|
|
125
|
+
yield* Result.await(writeLocalResolutionPinBoundary(() => mkdir(prismaDir, { recursive: true }), "create-directory", signal));
|
|
126
|
+
const pinPath = path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH);
|
|
127
|
+
const tmpPath = path.join(prismaDir, `local.${process.pid}.${Date.now()}.tmp`);
|
|
128
|
+
const serialized = yield* serializeLocalResolutionPin(pin);
|
|
129
|
+
yield* Result.await(writeLocalResolutionPinBoundary(() => writeFile(tmpPath, serialized, {
|
|
130
|
+
encoding: "utf8",
|
|
131
|
+
signal
|
|
132
|
+
}), "write-temp-file", signal));
|
|
133
|
+
yield* ensureLocalResolutionPinWriteNotAborted(signal);
|
|
134
|
+
yield* Result.await(writeLocalResolutionPinBoundary(() => rename(tmpPath, pinPath), "rename-temp-file", signal));
|
|
135
|
+
return Result.ok(void 0);
|
|
83
136
|
});
|
|
84
|
-
signal?.throwIfAborted();
|
|
85
|
-
await rename(tmpPath, pinPath);
|
|
86
137
|
}
|
|
87
138
|
async function ensureLocalResolutionPinGitignore(cwd, signal) {
|
|
88
139
|
const gitignorePath = path.join(cwd, ".gitignore");
|
|
89
140
|
let existing = null;
|
|
90
|
-
signal
|
|
91
|
-
|
|
92
|
-
|
|
141
|
+
const notAborted = ensureLocalResolutionPinGitignoreUpdateNotAborted(signal);
|
|
142
|
+
if (notAborted.isErr()) return Result.err(notAborted.error);
|
|
143
|
+
const existingResult = await Result.tryPromise({
|
|
144
|
+
try: () => readFile(gitignorePath, {
|
|
93
145
|
encoding: "utf8",
|
|
94
146
|
signal
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
147
|
+
}),
|
|
148
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("read", cause)
|
|
149
|
+
});
|
|
150
|
+
if (existingResult.isErr()) if (existingResult.error instanceof LocalResolutionPinGitignoreUpdateFailedError && existingResult.error.cause.code === "ENOENT") existing = null;
|
|
151
|
+
else return Result.err(existingResult.error);
|
|
152
|
+
else existing = existingResult.value;
|
|
153
|
+
if (existing === null) return writeLocalResolutionPinGitignore(gitignorePath, ".prisma/\n", signal);
|
|
154
|
+
if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return Result.ok(void 0);
|
|
155
|
+
return writeLocalResolutionPinGitignore(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, signal);
|
|
156
|
+
}
|
|
157
|
+
function ensureLocalResolutionPinWriteNotAborted(signal) {
|
|
158
|
+
return Result.try({
|
|
159
|
+
try: () => signal?.throwIfAborted(),
|
|
160
|
+
catch: (cause) => new LocalResolutionPinWriteAbortedError(cause)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function serializeLocalResolutionPin(pin) {
|
|
164
|
+
return Result.try({
|
|
165
|
+
try: () => `${JSON.stringify(pin, null, 2)}\n`,
|
|
166
|
+
catch: (cause) => new LocalResolutionPinSerializationError(cause)
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function writeLocalResolutionPinBoundary(run, operation, signal) {
|
|
170
|
+
return Result.tryPromise({
|
|
171
|
+
try: async () => {
|
|
172
|
+
await run();
|
|
173
|
+
},
|
|
174
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinWriteAbortedError(cause) : new LocalResolutionPinWriteFailedError(operation, cause)
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function ensureLocalResolutionPinGitignoreUpdateNotAborted(signal) {
|
|
178
|
+
return Result.try({
|
|
179
|
+
try: () => signal?.throwIfAborted(),
|
|
180
|
+
catch: (cause) => new LocalResolutionPinGitignoreUpdateAbortedError(cause)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function writeLocalResolutionPinGitignore(gitignorePath, contents, signal) {
|
|
184
|
+
return Result.tryPromise({
|
|
185
|
+
try: () => writeFile(gitignorePath, contents, {
|
|
101
186
|
encoding: "utf8",
|
|
102
187
|
signal
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return;
|
|
107
|
-
await writeFile(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, {
|
|
108
|
-
encoding: "utf8",
|
|
109
|
-
signal
|
|
188
|
+
}),
|
|
189
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("write", cause)
|
|
110
190
|
});
|
|
111
191
|
}
|
|
112
192
|
function isLocalResolutionPin(value) {
|
|
@@ -2,6 +2,7 @@ import { CliError, usageError } from "../../shell/errors.js";
|
|
|
2
2
|
import "../../shell/command-arguments.js";
|
|
3
3
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
|
|
4
4
|
import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
|
|
5
|
+
import { Result, matchError } from "better-result";
|
|
5
6
|
//#region src/lib/project/setup.ts
|
|
6
7
|
function isValidProjectSetupName(projectName) {
|
|
7
8
|
return projectName.trim().length > 0;
|
|
@@ -17,21 +18,63 @@ function resolveProjectForSetup(projectRef, projects, workspace) {
|
|
|
17
18
|
throw projectNotFoundError(projectRef, workspace);
|
|
18
19
|
}
|
|
19
20
|
async function bindProjectToDirectory(context, workspace, project, action) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
return Result.gen(async function* () {
|
|
22
|
+
yield* Result.await(writeLocalResolutionPin(context.runtime.cwd, {
|
|
23
|
+
workspaceId: workspace.id,
|
|
24
|
+
projectId: project.id
|
|
25
|
+
}, context.runtime.signal));
|
|
26
|
+
yield* Result.await(ensureLocalResolutionPinGitignore(context.runtime.cwd, context.runtime.signal));
|
|
27
|
+
return Result.ok({
|
|
28
|
+
workspace,
|
|
29
|
+
project,
|
|
30
|
+
directory: formatSetupDirectory(context.runtime.cwd),
|
|
31
|
+
localPin: {
|
|
32
|
+
path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
33
|
+
written: true
|
|
34
|
+
},
|
|
35
|
+
action
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function projectDirectoryBindingErrorToCliError(error) {
|
|
40
|
+
return matchError(error, {
|
|
41
|
+
LocalResolutionPinSerializationError: (error) => {
|
|
42
|
+
throw error;
|
|
32
43
|
},
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
LocalResolutionPinWriteAbortedError: (error) => {
|
|
45
|
+
throw error;
|
|
46
|
+
},
|
|
47
|
+
LocalResolutionPinWriteFailedError: (error) => localStateWriteFailedError(error, {
|
|
48
|
+
why: `The CLI could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
|
|
49
|
+
meta: {
|
|
50
|
+
pinPath: error.pinPath,
|
|
51
|
+
operation: error.operation
|
|
52
|
+
}
|
|
53
|
+
}),
|
|
54
|
+
LocalResolutionPinGitignoreUpdateAbortedError: (error) => {
|
|
55
|
+
throw error;
|
|
56
|
+
},
|
|
57
|
+
LocalResolutionPinGitignoreUpdateFailedError: (error) => localStateWriteFailedError(error, {
|
|
58
|
+
why: "The CLI could not update .gitignore to keep local Project binding state out of git.",
|
|
59
|
+
meta: {
|
|
60
|
+
gitignorePath: error.gitignorePath,
|
|
61
|
+
operation: error.operation
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function localStateWriteFailedError(error, options) {
|
|
67
|
+
return new CliError({
|
|
68
|
+
code: "LOCAL_STATE_WRITE_FAILED",
|
|
69
|
+
domain: "project",
|
|
70
|
+
summary: "Could not save local Project binding",
|
|
71
|
+
why: options.why,
|
|
72
|
+
fix: "Check that this directory is writable and that .prisma/local.json and .gitignore are not blocked by directories or permissions, then retry.",
|
|
73
|
+
debug: formatDebugDetails(error.cause),
|
|
74
|
+
meta: options.meta,
|
|
75
|
+
exitCode: 1,
|
|
76
|
+
nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli app deploy --project <id-or-name>"]
|
|
77
|
+
});
|
|
35
78
|
}
|
|
36
79
|
function toProjectSummary(project) {
|
|
37
80
|
return {
|
|
@@ -86,4 +129,4 @@ function formatDebugDetails(error) {
|
|
|
86
129
|
return typeof error === "string" ? error : null;
|
|
87
130
|
}
|
|
88
131
|
//#endregion
|
|
89
|
-
export { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary, validateProjectSetupNameText };
|
|
132
|
+
export { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary, validateProjectSetupNameText };
|