Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ older cached version. Prisma Compute does not support Deno deployments yet.
- `--deploy` / `--no-deploy`
- `--workspace <id-or-name>`
- `--yes`
- `--force`: overwrite generated starter and Prisma files in a non-empty directory. This replaces existing Prisma config, contract, and database-client files; back up edits first.
- `--force`: overwrite generated starter and Prisma files in a non-empty directory. This replaces existing Prisma config, contract, and database-client files; back up edits first. A non-empty standard `migrations` path is protected: use a new directory for a fresh starter, or continue working in the existing project with the Prisma CLI. Custom migration paths are not detected.
- `--verbose`
- `--json`

Expand Down
14 changes: 14 additions & 0 deletions src/commands/create-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,20 @@ export const collectCreateContext = Effect.fn("Create.collectContext")(function*
});
}

if (force && targetPathState.exists) {
const migrations = yield* inspectTargetPath(path.join(targetDirectory, "migrations"));
if (migrations.exists && !migrations.isEmptyDirectory) {
const message = `Target directory ${formatPathForDisplay(targetDirectory)} contains migration history. Create a starter in a new directory, or continue working in the existing project with the Prisma CLI. --force cannot overwrite a project with existing migrations.`;
yield* Effect.sync(() => cancel(message, { output }));
return yield* new CreateFailure({
stage: "collect_context",
reason: "target_has_migrations",
message,
errorReported: true,
});
}
}

const prismaSetupContext = yield* collectPrismaSetupContextEffect(input, {
projectDir: targetDirectory,
template,
Expand Down
1 change: 1 addition & 0 deletions src/create-outcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const CreateFailureReasonSchema = Schema.Literals([
"invalid_project_name",
"target_path_not_directory",
"target_directory_not_empty",
"target_has_migrations",
"unsupported_configuration",
"template_scaffold_failed",
"prisma_init_failed",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const createPrismaCommand = Command.make(
workspace: optionalString("workspace", "Prisma workspace id or name to deploy into"),
force: optionalBoolean(
"force",
"Overwrite generated starter and Prisma files in a non-empty directory",
"Overwrite generated starter and Prisma files; refuses a non-empty migrations path",
),
yes: optionalBoolean("yes", "Skip prompts and accept default choices"),
verbose: optionalBoolean("verbose", "Show verbose command output during setup"),
Expand Down
1 change: 1 addition & 0 deletions src/telemetry/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const expectedRejectionReasons = new Set<CreateFailureReason>([
"invalid_project_name",
"target_path_not_directory",
"target_directory_not_empty",
"target_has_migrations",
"unsupported_configuration",
"not_authenticated",
"workspace_missing",
Expand Down
30 changes: 30 additions & 0 deletions tests/e2e/create-prisma.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
readdir,
realpath,
rm,
stat,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
Expand Down Expand Up @@ -264,6 +265,35 @@ describe("create-prisma e2e", () => {
"Unrelated user file\n",
);
await runCommand(projectDir, ["bun", "run", "build"]);

await writeFile(contractPath, `${TEST_PSL_CONTRACT}\n// Keep this existing project edit\n`);
const preservedPaths = ["package.json", "prisma.config.ts", "src/prisma/contract.prisma"];
const migrationPaths = await readdir(path.join(projectDir, "migrations"), {
recursive: true,
});
for (const relativePath of migrationPaths) {
const filePath = path.join("migrations", relativePath);
if ((await stat(path.join(projectDir, filePath))).isFile()) {
preservedPaths.push(filePath);
}
}
const before = await Promise.all(
preservedPaths.map((filePath) => readFile(path.join(projectDir, filePath), "utf8")),
);
const completeRetry = await runCreatePrismaJson(rootDir, [...args, "--force"]);
expect(completeRetry.exitCode).toBe(1);
expect(completeRetry.result).toMatchObject({
ok: false,
error: { stage: "collect_context", message: expect.stringContaining("migration history") },
});
expect(await readdir(path.join(projectDir, "migrations"), { recursive: true })).toEqual(
migrationPaths,
);
expect(
await Promise.all(
preservedPaths.map((filePath) => readFile(path.join(projectDir, filePath), "utf8")),
),
).toEqual(before);
},
TEST_TIMEOUT,
);
Expand Down
9 changes: 7 additions & 2 deletions tests/telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ describe("create telemetry", () => {
});

test("separates expected input and environment rejections from technical failures", async () => {
for (const reason of ["target_directory_not_empty", "workspace_missing"] as const) {
for (const reason of [
"target_directory_not_empty",
"target_has_migrations",
"workspace_missing",
] as const) {
await trackCreateFailed({
input: createInput,
context: createContext,
Expand All @@ -94,9 +98,10 @@ describe("create telemetry", () => {
});
}
const calls = trackCliTelemetry.mock.calls as Array<[string, Record<string, unknown>]>;
expect(calls).toHaveLength(2);
expect(calls).toHaveLength(3);
expect(calls.map(([, properties]) => properties["failure-reason"])).toEqual([
"target_directory_not_empty",
"target_has_migrations",
"workspace_missing",
]);
for (const [, properties] of calls) {
Expand Down
Loading