Skip to content

Commit 8fa526b

Browse files
feat(cli): add init, the compute config formalizer (#108)
`prisma-cli init` writes a committed `prisma.compute.ts` for the app in the current directory. Deploy stays zero-config; init pins what deploy would infer so the setup is reviewable and stable for teammates and CI. It never overwrites an existing config, never scaffolds code (that's create-prisma's job), and needs no auth except for the optional link step. ``` $ prisma-cli init app acme-web package.json framework Next.js detected from package.json http port 3000 framework default ✔ Wrote prisma.compute.ts ? Link this directory to a Prisma Project now? (Y/n) Next steps: - prisma-cli app deploy ``` ```ts // generated prisma.compute.ts import { defineComputeConfig } from "@prisma/compute-sdk/config"; export default defineComputeConfig({ app: { name: "acme-web", framework: "nextjs", httpPort: 3000, }, }); ``` More examples: ```bash prisma-cli init --framework hono --entry src/index.ts # explicit framework prisma-cli init --project proj_123 # write + link, no prompts prisma-cli init --no-link --json # pure file generator (CI/agents) ``` Design notes - Pins identity only (`name`, `framework`, `httpPort`, `entry` for entrypoint frameworks, `region` only when passed). No `build` block, so build-command inference stays live until a user opts into owning it. `--framework custom` appends a commented `build` stub since custom artifacts require it. - `INIT_CONFIG_EXISTS` when any config exists up to the repo root (nested configs refused, steering monorepos to one root config); `INIT_DETECTION_FAILED` with the framework list in `--json`, an interactive picker otherwise. - Link failures after the write downgrade to warnings; the config stands and init exits 0. - `app deploy` success output now hints `Config prisma-cli init` when the deploy ran on inferred settings. Testing: 10 new integration tests (572 total green), lint/typecheck/build clean, plus a live run shown above.
1 parent c74cfcb commit 8fa526b

13 files changed

Lines changed: 1552 additions & 5 deletions

File tree

docs/product/command-spec.md

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,32 @@ The beta package includes these command groups:
1919
- `app`
2020
- `build` (includes `build logs`)
2121

22-
The beta package also includes one top-level utility command:
22+
The beta package also includes two top-level commands:
2323

2424
- `version`
25+
- `init`
2526

2627
`version` is intentionally outside the workflow groups: it reports CLI build and environment state, requires no auth, no project context, and no network, and is the canonical answer to "is this CLI installed and on the build I expect?"
2728

29+
`init` is a top-level workflow verb: it acts on the local project directory
30+
(writing the committed compute config) rather than managing a remote resource,
31+
so it sits beside the ORM's verb register (`generate`, `migrate`, `validate`)
32+
that the unified CLI will absorb. `init` never scaffolds application code;
33+
creating new apps is `create-prisma`'s job.
34+
2835
The Git repository connection slice uses the `git` group. It does not add a
2936
provider-specific `GitHub` group.
3037

3138
Out of scope for the current beta:
3239

33-
- `init`
3440
- `schema`
3541
- `migrate`
3642
- product-specific namespaces such as `compute`
3743

3844
## Global Rules
3945

4046
- Canonical shape is `prisma <group> <action>`.
41-
- `version` is the one top-level command outside that shape (see Scope above).
47+
- `version` and `init` are the top-level commands outside that shape (see Scope above).
4248
- Every command supports `--json`.
4349
- Shared global flags are:
4450
- `--json`
@@ -378,6 +384,49 @@ prisma-cli --version --json
378384

379385
`prisma-cli version` is the richer environment report; `prisma-cli --version` is the terse one-liner. Both report the same `cli.version`. Use the flag for quick checks, the subcommand for support tickets and bug reports.
380386

387+
## `prisma-cli init --framework <nextjs|nuxt|astro|hono|nestjs|tanstack-start|bun|custom> --entry <path> --http-port <port> --region <region> --name <app-name> --link --no-link --project <id-or-name> --install --no-install`
388+
389+
Purpose:
390+
391+
- write a committed `prisma.compute.ts` for the app in this directory
392+
393+
Behavior:
394+
395+
- init is the config formalizer: `app deploy` works with zero config, and init writes down what deploy would infer so the setup is committed, reviewable, and stable for teammates and CI
396+
- writing the config requires no auth and no network; linking is the only remote step
397+
- fails with `INIT_CONFIG_EXISTS` when a compute config already exists in the invocation directory or any ancestor up to the repository or workspace root; the error names the existing file, and init never overwrites or merges — editing a committed config is the user's editor's job
398+
- detects the framework from the same registry and signals `app deploy` uses; explicit `--framework` wins over detection
399+
- `--entry` sets the source entrypoint for entrypoint frameworks (Bun, Hono); `--http-port` overrides the framework default port; `--name` overrides the app name inferred from `package.json#name` or the directory name
400+
- previews the resolved values with per-value source annotations (`detected`, `framework default`, `package.json`, `flag`) before writing; in interactive mode the preview is followed by an optional adjust step for framework and HTTP port, and `--yes` accepts the preview as shown
401+
- the generated config pins the app's identity: `name`, `framework`, and `httpPort` always; `entry` when the framework consumes a source entrypoint; `region` only when `--region` is passed, because pinning a region the user did not choose would silently place new apps
402+
- the generated config does not include a `build` block: build settings stay inferred (and shown with their sources by deploy) until the user adds one, which keeps package-manager and build-script inference live
403+
- init never scaffolds application code, never creates schema or database resources, and never deploys
404+
- with `--framework custom`, the config includes a commented `build` stub, since custom artifacts require `build.outputDirectory` and `build.entrypoint` before deploy can use them
405+
- when detection fails and no `--framework` is passed: interactive mode prompts for the framework from the supported list; non-interactive and `--json` mode fail with `INIT_DETECTION_FAILED`, with `nextActions` enumerating the `--framework` choices
406+
- types step, after the config is written: the generated config's typed import (`@prisma/compute-sdk/config`) is resolved by the CLI at deploy time without a local install, so a local `@prisma/compute-sdk` devDependency exists purely for editor types
407+
- when the package is already a dependency or devDependency, the step is a no-op
408+
- interactive mode asks `Install @prisma/compute-sdk for config types?` (default yes) and runs the detected package manager's add command (`pnpm add -D`, `bun add -d`, `yarn add -D`, `npm install -D`)
409+
- `--install` runs the install without prompting; `--no-install` skips the step; non-interactive and `--json` mode skip by default
410+
- a skipped, declined, or failed install downgrades to a hint or warning with the exact add command in `nextSteps`; the config write stands and init exits 0
411+
- a directory without a `package.json` skips the step with the hint
412+
- link step, after the config is written:
413+
- interactive mode asks `Link this directory to a Prisma Project now? (Y/n)` when the directory has no project binding; accepting enters the same picker `project link` uses
414+
- `--no-link` suppresses the question; `--link` requires the step; `--project <id-or-name>` links to that project without prompting
415+
- link failures and cancellations after the config is written downgrade to warnings and `nextSteps`; the config write stands and init exits 0
416+
- `nextSteps` includes the deploy command, plus the project link command when the directory is still unlinked
417+
- user-facing command hints in init output (next steps, link hints, error recovery commands) use the package runner detected from the project, such as `pnpm dlx @prisma/cli@latest project link` or `npx -y @prisma/cli@latest app deploy`, matching the `agent` group's convention
418+
- in `--json`, `result` includes `configPath`, the written `app` values, per-value `settings` sources, and `link` state; `--json` never prompts
419+
420+
Examples:
421+
422+
```bash
423+
prisma-cli init
424+
prisma-cli init --framework hono --entry src/index.ts
425+
prisma-cli init --name api --http-port 8080 --no-link
426+
prisma-cli init --project proj_123
427+
prisma-cli init --json
428+
```
429+
381430
## `<runner> @prisma/cli@latest agent install --agent <agent> --all-agents --skill <skill> --global --copy`
382431

383432
Purpose:
@@ -1333,6 +1382,7 @@ Behavior:
13331382
- after setup, deploy prints `Deploying to <Project> / <Branch> / <App>`; later deploys print a compact target header such as `Deploying ./j1 to j1 / main / j1`
13341383
- deploy progress uses short stage copy (`Building locally...`, `Built <size>`, `Uploading...`, `Uploaded`, `Deploying...`, `Deployed`) and never prints `Status: running` or `Deployment is running at ...`
13351384
- success human output prints `Live in <duration>`, the URL on its own line, and `Logs prisma-cli app logs`
1385+
- when the deploy resolved its settings without a compute config, success human output adds a `Config` hint line with the runner-formatted init command (such as `pnpm dlx @prisma/cli@latest init`), pointing at the command that pins the inferred settings; the hint is omitted once a config file is discovered
13361386
- with `--no-promote`, success human output instead prints `Built <deployment-id> in <duration> (not promoted)`, the candidate URL on its own line, a note that the live deployment is unchanged, and a `Promote prisma-cli app promote <deployment-id>` next step
13371387
- accepts repeated `--env NAME=VALUE` flags and dotenv file paths such as `--env .env`
13381388
- supports `--db` to create a new empty Prisma Postgres database and write `DATABASE_URL` and `DIRECT_URL` through the existing `project env` storage; the CLI never runs schema or migration commands — applying the schema stays with the user's own tooling
@@ -1728,6 +1778,62 @@ prisma-cli app logs
17281778
prisma-cli app logs --deployment dep_123
17291779
```
17301780

1781+
## `prisma-cli build list [app] --app <name> --project <id-or-name> --branch <name> --limit <n>`
1782+
1783+
Status: blocked on Management API rollout. The `GET /v1/apps/{appId}/builds`
1784+
endpoint exists in the control plane but is not yet deployed or published in
1785+
`@prisma/management-api-sdk`. This section is normative for the implementation
1786+
that lands once the SDK exposes the endpoint.
1787+
1788+
Purpose:
1789+
1790+
- list the git build jobs for an app, so build ids are discoverable without the Console
1791+
1792+
Behavior:
1793+
1794+
- requires auth and project context
1795+
- resolves the selected app exactly like the other app management commands: `[app]` target argument, `--app`, compute config target, locally selected app, inferred name; never creates apps or branches
1796+
- resolves the branch it reads like management commands: explicit `--branch`, active Git branch when it exists in the project, then the project's default branch
1797+
- lists builds newest first: build id, state (`pending`, `running`, `succeeded`, `failed`, `cancelled`), source (`webhook`, `setup`, `manual`), Git branch, short commit sha, created and finished timestamps, and the produced deployment id when the build reached that stage
1798+
- build ids are the ids `build logs <build-id>` accepts
1799+
- `--limit <n>` caps the number of returned builds; JSON output includes `pagination.nextCursor` and `pagination.hasMore` so agents can page
1800+
- read-only; never prints secret values
1801+
- `nextSteps` includes `prisma-cli build logs <build-id>` for the newest build
1802+
- fails with the standard app selection errors (`APP_AMBIGUOUS`, app not found) when the target cannot be resolved safely
1803+
1804+
Examples:
1805+
1806+
```bash
1807+
prisma-cli build list
1808+
prisma-cli build list --app my-app --limit 50
1809+
prisma-cli build list --json
1810+
```
1811+
1812+
## `prisma-cli build show <build-id>`
1813+
1814+
Status: blocked on Management API rollout, same as `build list`; the backing
1815+
endpoint is `GET /v1/builds/{buildId}`.
1816+
1817+
Purpose:
1818+
1819+
- show one build in detail
1820+
1821+
Behavior:
1822+
1823+
- requires auth
1824+
- takes a build id, as shown by `build list`, the Console build view, and git-push output
1825+
- authorization matches `build logs`: access stays with the workspace that owned the build when it ran, and an unknown or foreign build id fails with an indistinguishable `BUILD_NOT_FOUND`
1826+
- shows state, source, Git branch, commit sha, created/started/finished timestamps, the error message when the build failed, and the produced deployment id and deployed URL when present
1827+
- read-only; never prints secret values
1828+
- `nextSteps` includes `prisma-cli build logs <build-id>`
1829+
1830+
Examples:
1831+
1832+
```bash
1833+
prisma-cli build show cmcz3v6ft0a1b2c3d
1834+
prisma-cli build show cmcz3v6ft0a1b2c3d --json
1835+
```
1836+
17311837
## `prisma-cli build logs <build-id> --follow --cursor <cursor>`
17321838

17331839
Purpose:

docs/product/error-conventions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ These codes are the minimum stable set for the MVP:
187187
- `BUILD_SETTINGS_MIGRATION_REQUIRED`
188188
- `BUILD_SETTINGS_UNSUPPORTED`
189189
- `FRAMEWORK_NOT_DETECTED`
190+
- `INIT_CONFIG_EXISTS`
191+
- `INIT_DETECTION_FAILED`
190192
- `DEPLOYMENT_NOT_FOUND`
191193
- `NO_DEPLOYMENTS`
192194
- `NO_PREVIOUS_DEPLOYMENT`
@@ -258,6 +260,8 @@ Recommended meanings:
258260
- `BUILD_SETTINGS_MIGRATION_REQUIRED`: a legacy `prisma.app.json` contains custom build settings that must move into the `build` block of `prisma.compute.ts`
259261
- `BUILD_SETTINGS_UNSUPPORTED`: a compute config `build` block targets a framework whose SDK strategy does not consume committed build settings
260262
- `FRAMEWORK_NOT_DETECTED`: app deploy could not detect a supported Beta framework and no explicit framework/build type was provided
263+
- `INIT_CONFIG_EXISTS`: a compute config already exists in this directory or an ancestor; init never overwrites or merges
264+
- `INIT_DETECTION_FAILED`: no supported framework detected and no --framework passed; `meta.frameworks` lists the valid values
261265
- `DEPLOYMENT_NOT_FOUND`: requested deployment id does not exist
262266
- `NO_DEPLOYMENTS`: command resolved a branch or app but found no deployments
263267
- `NO_PREVIOUS_DEPLOYMENT`: rollback could not find an earlier deployment for the selected app

docs/product/resource-model.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Rules:
3939
- Public Beta does not read or write committed config files such as `prisma.config.ts` or `.prisma/settings.json` for project resolution
4040
- `.prisma/local.json` is a gitignored local pin/cache for Workspace and Project IDs; it is not a declarative repo config file. When a `prisma.compute.ts` is discovered (nearest config from the invocation directory up to the repository or workspace root), the pin and the CLI state cache (`.prisma/cli/state.json`) are read and written in the config file's directory; without a config they stay in the invocation directory
4141
- `prisma.compute.ts` is a committed deploy-defaults file; it must not contain Workspace, Project, Branch, env-secret, or credential resolution state
42+
- `init` is the only command that writes `prisma.compute.ts`, and it never overwrites an existing one; deploy reads the config but never writes it
4243
- Project setup is explicit: users choose an existing Project or explicitly create a new one before remote work starts
4344
- `app deploy` may orchestrate Project setup, but it must not silently choose or create Project scope
4445
- everything under a project happens in a branch

packages/cli/src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createBranchCommand } from "./commands/branch";
99
import { createBuildCommand } from "./commands/build";
1010
import { createDatabaseCommand } from "./commands/database";
1111
import { createGitCommand } from "./commands/git";
12+
import { createInitCommand } from "./commands/init";
1213
import { createProjectCommand } from "./commands/project";
1314
import { createVersionCommand } from "./commands/version";
1415
import { runVersion } from "./controllers/version";
@@ -83,6 +84,7 @@ export function createProgram(runtime: CliRuntime): Command {
8384
program.name("prisma").showSuggestionAfterError();
8485

8586
program.addCommand(createVersionCommand(runtime));
87+
program.addCommand(createInitCommand(runtime));
8688
program.addCommand(createAgentCommand(runtime));
8789
program.addCommand(createAuthCommand(runtime));
8890
program.addCommand(createProjectCommand(runtime));
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Command, Option } from "commander";
2+
3+
import { runInit } from "../../controllers/init";
4+
import { renderInit, serializeInit } from "../../presenters/init";
5+
import { attachCommandDescriptor } from "../../shell/command-meta";
6+
import { runCommand } from "../../shell/command-runner";
7+
import { addGlobalFlags } from "../../shell/global-flags";
8+
import { type CliRuntime, configureRuntimeCommand } from "../../shell/runtime";
9+
import type { InitResult } from "../../types/init";
10+
11+
export function createInitCommand(runtime: CliRuntime): Command {
12+
const command = attachCommandDescriptor(
13+
configureRuntimeCommand(new Command("init"), runtime),
14+
"init",
15+
);
16+
17+
command
18+
.addOption(
19+
new Option(
20+
"--framework <framework>",
21+
"Framework override; detected when omitted",
22+
),
23+
)
24+
.addOption(
25+
new Option(
26+
"--entry <path>",
27+
"Source entrypoint for entrypoint frameworks (Bun, Hono)",
28+
),
29+
)
30+
.addOption(new Option("--http-port <port>", "HTTP port the app listens on"))
31+
.addOption(
32+
new Option(
33+
"--region <region>",
34+
"Region used when deploy creates the app",
35+
),
36+
)
37+
.addOption(new Option("--name <app-name>", "App name"))
38+
.addOption(new Option("--link", "Link this directory to a Project"))
39+
.addOption(new Option("--no-link", "Skip the Project link step"))
40+
.addOption(
41+
new Option("--project <id-or-name>", "Project to link this directory to"),
42+
)
43+
.addOption(
44+
new Option("--install", "Install @prisma/compute-sdk for config types"),
45+
)
46+
.addOption(new Option("--no-install", "Skip the types install step"));
47+
addGlobalFlags(command);
48+
49+
command.action(async (options) => {
50+
const flags = options as {
51+
framework?: string;
52+
entry?: string;
53+
httpPort?: string;
54+
region?: string;
55+
name?: string;
56+
link?: boolean;
57+
project?: string;
58+
install?: boolean;
59+
};
60+
61+
await runCommand<InitResult>(
62+
runtime,
63+
"init",
64+
options as Record<string, unknown>,
65+
(context) =>
66+
runInit(context, {
67+
framework: flags.framework,
68+
entry: flags.entry,
69+
httpPort: flags.httpPort,
70+
region: flags.region,
71+
name: flags.name,
72+
link: flags.link,
73+
project: flags.project,
74+
install: flags.install,
75+
}),
76+
{
77+
renderHuman: (context, descriptor, result) =>
78+
renderInit(context, descriptor, result),
79+
renderJson: (result) => serializeInit(result),
80+
},
81+
);
82+
});
83+
84+
return command;
85+
}

packages/cli/src/controllers/app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3777,7 +3777,7 @@ async function resolveDeployBranch(
37773777
};
37783778
}
37793779

3780-
interface ResolvedDeployFramework {
3780+
export interface ResolvedDeployFramework {
37813781
key: string;
37823782
buildType: FrameworkBuildType;
37833783
displayName: string;
@@ -4074,7 +4074,7 @@ async function resolveDeployEntrypoint(
40744074
}
40754075
}
40764076

4077-
async function detectDeployFramework(
4077+
export async function detectDeployFramework(
40784078
cwd: string,
40794079
signal: AbortSignal,
40804080
): Promise<ResolvedDeployFramework | null> {

0 commit comments

Comments
 (0)