@prisma/cli 3.0.0-dev.107.1 → 3.0.0-dev.110.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/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 { createBuildCommand } from "./commands/build/index.js";
12
13
  import { createDatabaseCommand } from "./commands/database/index.js";
13
14
  import { createGitCommand } from "./commands/git/index.js";
14
15
  import { createProjectCommand } from "./commands/project/index.js";
@@ -52,6 +53,7 @@ function createProgram(runtime) {
52
53
  program.addCommand(createProjectCommand(runtime));
53
54
  program.addCommand(createGitCommand(runtime));
54
55
  program.addCommand(createBranchCommand(runtime));
56
+ program.addCommand(createBuildCommand(runtime));
55
57
  program.addCommand(createDatabaseCommand(runtime));
56
58
  program.addCommand(createAppCommand(runtime));
57
59
  return program;
@@ -66,7 +66,7 @@ function createRunCommand(runtime) {
66
66
  }
67
67
  function createDeployCommand(runtime) {
68
68
  const command = attachCommandDescriptor(configureRuntimeCommand(new Command("deploy"), runtime), "app.deploy");
69
- 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("--create-project <name>", "Create and link a new Project before deploying")).addOption(new Option("--branch <name>", "Branch name")).addOption(new Option("--framework <name>", "Framework to deploy").choices([...FRAMEWORK_KEYS])).addOption(new Option("--entry <path>", "Entrypoint path for Bun deploys")).addOption(new Option("--http-port <port>", "HTTP port override for the deployed app")).addOption(new Option("--region <region>", "Region for a newly created app; existing apps keep their region")).addOption(new Option("--env <name=value|file>", "Environment variable assignment or dotenv file").argParser(collectRepeatableValues)).addOption(new Option("--db", "Create and wire a Prisma Postgres database for this deploy target")).addOption(new Option("--no-db", "Skip database setup")).addOption(new Option("--prod", "Confirm intent to deploy to production"));
69
+ 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("--create-project <name>", "Create and link a new Project before deploying")).addOption(new Option("--branch <name>", "Branch name")).addOption(new Option("--framework <name>", "Framework to deploy").choices([...FRAMEWORK_KEYS])).addOption(new Option("--entry <path>", "Entrypoint path for Bun deploys")).addOption(new Option("--http-port <port>", "HTTP port override for the deployed app")).addOption(new Option("--region <region>", "Region for a newly created app; existing apps keep their region")).addOption(new Option("--env <name=value|file>", "Environment variable assignment or dotenv file").argParser(collectRepeatableValues)).addOption(new Option("--db", "Create and wire a Prisma Postgres database for this deploy target")).addOption(new Option("--no-db", "Skip database setup")).addOption(new Option("--prod", "Confirm intent to deploy to production")).addOption(new Option("--no-promote", "Build the new deployment without promoting it to live (promote later with app promote <id>)"));
70
70
  addGlobalFlags(command);
71
71
  command.action(async (configTarget, options) => {
72
72
  const appName = options.app;
@@ -79,6 +79,7 @@ function createDeployCommand(runtime) {
79
79
  const projectRef = options.project;
80
80
  const createProjectName = options.createProject;
81
81
  const prod = options.prod;
82
+ const noPromote = options.promote === false;
82
83
  const db = options.db;
83
84
  const hasDbConflict = hasFlag(runtime.argv, "--db") && hasFlag(runtime.argv, "--no-db");
84
85
  await runCommand(runtime, "app.deploy", options, (context) => {
@@ -93,6 +94,7 @@ function createDeployCommand(runtime) {
93
94
  region,
94
95
  envAssignments,
95
96
  prod: prod === true,
97
+ noPromote,
96
98
  db,
97
99
  configTarget
98
100
  });
@@ -0,0 +1,29 @@
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 { runStreamingCommand } from "../../shell/command-runner.js";
5
+ import { runBuildLogs } from "../../controllers/build.js";
6
+ import { Command, Option } from "commander";
7
+ //#region src/commands/build/index.ts
8
+ function createBuildCommand(runtime) {
9
+ const build = attachCommandDescriptor(configureRuntimeCommand(new Command("build"), runtime), "build");
10
+ addCompactGlobalFlags(build);
11
+ build.addCommand(createLogsCommand(runtime));
12
+ return build;
13
+ }
14
+ function createLogsCommand(runtime) {
15
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("logs"), runtime), "build.logs");
16
+ command.argument("<buildId>", "Build id from a git-push or Console deploy").addOption(new Option("--follow", "Keep the connection open and stream new logs as the build runs")).addOption(new Option("--cursor <cursor>", "Resume from a prior terminal cursor"));
17
+ addGlobalFlags(command);
18
+ command.action(async (buildId, options) => {
19
+ const follow = options.follow;
20
+ const cursor = options.cursor;
21
+ await runStreamingCommand(runtime, "build.logs", options, (context) => runBuildLogs(context, buildId, {
22
+ follow,
23
+ cursor
24
+ }), { emitJsonSuccessEvent: false });
25
+ });
26
+ return command;
27
+ }
28
+ //#endregion
29
+ export { createBuildCommand };
@@ -329,7 +329,8 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
329
329
  });
330
330
  framework = customized.framework;
331
331
  runtime = customized.runtime;
332
- const productionDeployGate = await enforceProductionDeployGate(context, provider, {
332
+ const noPromote = options?.noPromote === true;
333
+ const productionDeployGate = noPromote ? { firstProductionDeploy: false } : await enforceProductionDeployGate(context, provider, {
333
334
  appId: selectedApp.appId,
334
335
  appName: selectedApp.displayName,
335
336
  branchKind: target.branch.kind,
@@ -367,6 +368,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
367
368
  buildSettings: buildSettingsResolution.settings,
368
369
  portMapping,
369
370
  envVars,
371
+ skipPromote: noPromote,
370
372
  interaction: void 0,
371
373
  signal: context.runtime.signal,
372
374
  progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
@@ -378,7 +380,8 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
378
380
  id: deployResult.app.id,
379
381
  name: deployResult.app.name
380
382
  });
381
- await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, deployResult.deployment.id);
383
+ const knownLiveDeploymentId = deployResult.promoted ? deployResult.deployment.id : deployResult.app.liveDeploymentId;
384
+ if (knownLiveDeploymentId) await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, knownLiveDeploymentId);
382
385
  return {
383
386
  command: "app.deploy",
384
387
  result: {
@@ -392,6 +395,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
392
395
  name: deployResult.app.name
393
396
  },
394
397
  deployment: deployResult.deployment,
398
+ promoted: deployResult.promoted,
395
399
  deploySettings: {
396
400
  config: {
397
401
  path: buildSettingsResolution.relativeConfigPath,
@@ -424,7 +428,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
424
428
  ...legacyWarnings,
425
429
  ...branchDatabaseSetup.warnings
426
430
  ],
427
- nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
431
+ nextSteps: deployResult.promoted ? ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`] : [`prisma-cli app promote ${deployResult.deployment.id}`, `prisma-cli app show-deploy ${deployResult.deployment.id}`]
428
432
  };
429
433
  }
430
434
  async function resolveDeployBuildSettings(options) {
@@ -0,0 +1,88 @@
1
+ import { CliError, authRequiredError } from "../shell/errors.js";
2
+ import { writeJsonEvent } from "../shell/output.js";
3
+ import { requireComputeAuth } from "../lib/auth/guard.js";
4
+ //#region src/controllers/build.ts
5
+ async function runBuildLogs(context, buildId, options = {}) {
6
+ const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
7
+ if (!client) throw authRequiredError(["prisma-cli auth login"]);
8
+ if (!context.flags.json && !context.flags.quiet) context.output.stderr.write(`build logs → Streaming logs for build ${buildId}\n\n`);
9
+ const { data, response } = await client.GET("/v1/builds/{buildId}/logs", {
10
+ params: {
11
+ path: { buildId },
12
+ query: {
13
+ ...options.follow ? { follow: "true" } : {},
14
+ ...options.cursor ? { cursor: options.cursor } : {}
15
+ }
16
+ },
17
+ parseAs: "stream",
18
+ signal: context.runtime.signal
19
+ });
20
+ const body = data;
21
+ if (!response.ok || !body) throw buildLogsRequestError(buildId, response.status);
22
+ let sawError = false;
23
+ await forEachNdjsonRecord(body, (record) => {
24
+ if (record.type === "terminal" && record.kind === "error") sawError = true;
25
+ writeBuildLogRecord(context, record);
26
+ });
27
+ if (sawError) process.exitCode = 1;
28
+ }
29
+ function writeBuildLogRecord(context, record) {
30
+ if (context.flags.json) {
31
+ writeJsonEvent(context.output, {
32
+ type: record.type,
33
+ command: "build.logs",
34
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35
+ data: record
36
+ });
37
+ return;
38
+ }
39
+ if (record.type === "log") {
40
+ const stream = record.source === "stderr" || record.level === "error" ? context.output.stderr : context.output.stdout;
41
+ stream.write(record.text);
42
+ if (!record.text.endsWith("\n")) stream.write("\n");
43
+ return;
44
+ }
45
+ if (record.code !== "end") context.output.stderr.write(`${record.message}\n`);
46
+ }
47
+ /** Reads a newline-delimited JSON body line by line, parsing each into a record. */
48
+ async function forEachNdjsonRecord(body, onRecord) {
49
+ const reader = body.getReader();
50
+ const decoder = new TextDecoder();
51
+ let buffer = "";
52
+ for (;;) {
53
+ const { done, value } = await reader.read();
54
+ if (value) buffer += decoder.decode(value, { stream: true });
55
+ let newlineIndex = buffer.indexOf("\n");
56
+ while (newlineIndex !== -1) {
57
+ const line = buffer.slice(0, newlineIndex).trim();
58
+ buffer = buffer.slice(newlineIndex + 1);
59
+ if (line) onRecord(JSON.parse(line));
60
+ newlineIndex = buffer.indexOf("\n");
61
+ }
62
+ if (done) {
63
+ const tail = buffer.trim();
64
+ if (tail) onRecord(JSON.parse(tail));
65
+ return;
66
+ }
67
+ }
68
+ }
69
+ function buildLogsRequestError(buildId, status) {
70
+ if (status === 404) return new CliError({
71
+ code: "BUILD_NOT_FOUND",
72
+ domain: "app",
73
+ summary: `Build ${buildId} was not found`,
74
+ why: "The build does not exist, or your workspace does not have access to it.",
75
+ fix: "Check the build id, or run prisma-cli auth login to switch to the workspace that owns it.",
76
+ exitCode: 1
77
+ });
78
+ return new CliError({
79
+ code: "BUILD_LOGS_FAILED",
80
+ domain: "app",
81
+ summary: `Failed to read logs for build ${buildId}`,
82
+ why: `The Management API returned HTTP ${status}.`,
83
+ fix: "Retry the command, or rerun with --trace for more detail.",
84
+ exitCode: 1
85
+ });
86
+ }
87
+ //#endregion
88
+ export { runBuildLogs };
@@ -174,6 +174,7 @@ function createAppProvider(client, options) {
174
174
  region: resolvedApp.region,
175
175
  portMapping: options.portMapping,
176
176
  envVars: options.envVars,
177
+ skipPromote: options.skipPromote,
177
178
  timeoutSeconds: 120,
178
179
  pollIntervalMs: 2e3,
179
180
  interaction: options.interaction,
@@ -188,14 +189,16 @@ function createAppProvider(client, options) {
188
189
  id: deployed.appId,
189
190
  name: deployed.appName,
190
191
  region: deployed.region ?? null,
191
- liveDeploymentId: deployed.deploymentId,
192
+ liveDeploymentId: deployed.promoted ? deployed.deploymentId : deployed.previousDeploymentId,
192
193
  liveUrl: toAbsoluteUrl(deployed.appEndpointDomain ?? null)
193
194
  },
194
195
  deployment: {
195
196
  id: deployed.deploymentId,
196
197
  status: "running",
197
- url: toAbsoluteUrl(deployed.appEndpointDomain ?? deployed.deploymentEndpointDomain ?? null)
198
- }
198
+ url: toAbsoluteUrl(deployed.appEndpointDomain ?? deployed.deploymentEndpointDomain ?? null),
199
+ live: deployed.promoted
200
+ },
201
+ promoted: deployed.promoted
199
202
  };
200
203
  },
201
204
  async updateAppEnv(options) {
@@ -31,11 +31,17 @@ function serializeAppBuild(result) {
31
31
  function renderAppDeploy(context, descriptor, result, options) {
32
32
  const logsCommand = options?.logsTarget ? `prisma-cli app logs ${options.logsTarget}` : "prisma-cli app logs";
33
33
  return [
34
- `Live in ${formatDuration(result.durationMs)}`,
35
- ...result.deployment.url ? [context.ui.link(result.deployment.url)] : [],
34
+ ...result.promoted ? [`Live in ${formatDuration(result.durationMs)}`, ...result.deployment.url ? [context.ui.link(result.deployment.url)] : []] : [
35
+ `Built ${result.deployment.id} in ${formatDuration(result.durationMs)} (not promoted)`,
36
+ ...result.deployment.url ? [context.ui.link(result.deployment.url)] : [],
37
+ context.ui.dim("The live deployment is unchanged.")
38
+ ],
36
39
  ...renderBranchDatabaseDeploySummary(context, result),
37
40
  "",
38
- ...renderDeployOutputRows(context.ui, [{
41
+ ...renderDeployOutputRows(context.ui, [...result.promoted ? [] : [{
42
+ label: "Promote",
43
+ value: `prisma-cli app promote ${result.deployment.id}`
44
+ }], {
39
45
  label: "Logs",
40
46
  value: logsCommand
41
47
  }]),
@@ -195,6 +195,22 @@ const DESCRIPTORS = [
195
195
  description: "View your Platform branches",
196
196
  examples: ["prisma-cli branch list"]
197
197
  },
198
+ {
199
+ id: "build",
200
+ path: ["prisma", "build"],
201
+ description: "Inspect builds",
202
+ examples: ["prisma-cli build logs <build_id>"]
203
+ },
204
+ {
205
+ id: "build.logs",
206
+ path: [
207
+ "prisma",
208
+ "build",
209
+ "logs"
210
+ ],
211
+ description: "Stream the logs for a build",
212
+ examples: ["prisma-cli build logs <build_id>", "prisma-cli build logs <build_id> --follow"]
213
+ },
198
214
  {
199
215
  id: "database",
200
216
  path: ["prisma", "database"],
@@ -75,12 +75,12 @@ async function renderBestEffortCommandDiagnostics(context, options) {
75
75
  return [];
76
76
  }
77
77
  }
78
- async function runStreamingCommand(runtime, commandName, options, handler) {
78
+ async function runStreamingCommand(runtime, commandName, options, handler, streamOptions) {
79
79
  const flags = resolveGlobalFlags(runtime.argv, options);
80
80
  const context = await createCommandContext(runtime, flags);
81
81
  try {
82
82
  await handler(context);
83
- if (flags.json) writeJsonEvent(context.output, {
83
+ if (flags.json && (streamOptions?.emitJsonSuccessEvent ?? true)) writeJsonEvent(context.output, {
84
84
  type: "success",
85
85
  command: commandName,
86
86
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.107.1",
3
+ "version": "3.0.0-dev.110.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {