Skip to content

Add prisma app env set/list/unset namespace#4

Draft
kristof-siket wants to merge 6 commits intomainfrom
ptl-1475-add-prisma-app-env-setlistunset-to-prisma-cli
Draft

Add prisma app env set/list/unset namespace#4
kristof-siket wants to merge 6 commits intomainfrom
ptl-1475-add-prisma-app-env-setlistunset-to-prisma-cli

Conversation

@kristof-siket
Copy link
Copy Markdown

Linear: PTL-1475 (Compute Public Beta launch — M5: Configure)

Summary

Adds a prisma app env namespace so CLI users can author environment variables against the new platform-managed /v1/environment-variables API. Without this, the CLI has no self-serve write path against the platform-authoritative env-var store; app update-env / app list-env operate on the legacy Foundry-version env-var path, which is no longer the source of truth after the deploy path was made platform-authoritative in pdp-control-plane#3849.

New verbs:

  • prisma app env set KEY=VALUE — create or replace a variable on a project template (--class production / --class preview) or a branch override (--branch <name>). Idempotent upsert: existing rows are replaced via PATCH, new rows are created via POST.
  • prisma app env list — list metadata for a scope (no values, FR15). Defaults to --class production when no scope flag is provided.
  • prisma app env unset KEY — looks the row up by natural key and DELETEs it.

Scope flag rules (FR21)

  • --class <production|preview> targets a project template.
  • --branch <name> targets a per-branch override (always against the preview class — the platform forbids overrides on production). Resolves the name to a Branch ID via GET /v1/projects/{projectId}/branches?gitName=.
  • --class and --branch are mutually exclusive.
  • set / unset require an explicit scope so the CLI never silently writes to production.
  • list with neither flag defaults to --class production.

Surface policy

  • No pull verb in Beta — under FR15 (never-reveal) the platform never returns stored plaintext, so pull would always write a zero-value file. Decided in spec § Decisions Log Q17.
  • All list output is metadata-only (key, id, last updated). The plaintext value never appears in any response body, log, or rendered field anywhere in the namespace.

Legacy commands

app update-env and app list-env continue to work for backward compatibility but emit a deprecation banner to stderr pointing at the new commands. The banner is suppressed under --json so machine consumers keep their JSON channel clean. Removal is deferred — see the open question below.

Branch-override writes (current scope)

The Management API's POST / PATCH request bodies on /v1/environment-variables don't yet accept a branchId field — branch-override writes are scheduled as a follow-up extension to the env-var route. Until that lands, this PR surfaces a focused "feature unavailable" error when the user runs app env set / unset with --branch. List against --branch works because the GET endpoint already supports the filter.

Diff / import

diff and import are deferred to follow-up tickets — the spec lists them as part of FR21/FR22 but they're large enough on their own (especially import's file-parsing surface) that bundling them here would have made review unwieldy. Will track as separate PTL tickets.

Decision pending

cc @luan @alexey — the legacy app update-env / app list-env deprecation in this PR is warning-only. Plan task 3B.1 calls for an explicit decision on whether to remove the legacy commands at the end of Beta or carry them indefinitely. This PR does not remove them; happy to follow up with a removal PR once we align.

Coordination

Depends on @prisma/management-api-sdk ≥ 1.27.0 (already published; introduced by pdp-control-plane#3827 + #3831). The dep bump from ^1.24.0 to ^1.27.0 is included in this PR. The compute-sdk peer-dep range (>=1.23.0) accepts 1.27.0 unchanged.

Test plan

  • pnpm test — 26 test files, 166 tests pass (151 baseline + 15 new in tests/app-env.test.ts)
  • pnpm --filter @prisma/cli build — builds clean
  • pnpm --filter @prisma/cli exec tsc --noEmit — no new type errors (one preexisting publish-prep.test.ts declaration-file error unaffected by this change)
  • Manual smoke: pnpm prisma app env --help, set --help, list --help, unset --help; flag-rule errors render with the expected next-step examples
  • Manual smoke against a venus-flagged workspace — pending preview-deployment availability of the API endpoints. Will verify on dev as soon as the SDK 1.27 bump finishes propagating.

Acceptance criteria (AC10 / TC-13)

  • prisma app env set STRIPE_KEY=sk_test_... --class production succeeds; the variable appears in prisma app env list --class production (covered by integration tests against a mocked ManagementApiClient).
  • prisma app env set STRIPE_KEY=val --class production --branch feature/auth is rejected with a clear mutually-exclusive-flags error.
  • prisma app env set STRIPE_KEY=val (neither flag) is rejected with a clear "requires --class or --branch" error.
  • prisma app env unset STRIPE_KEY --branch feature-auth removes the override (when the API supports it; covered by happy-path test against --class, blocked-path test against --branch for now).
  • All list output is metadata-only (assertion: result JSON contains no value field).
  • Legacy commands print the deprecation banner to stderr and are silent under --json.

PTL-1475 / Compute Public Beta launch — M5: Configure.

The platform-managed /v1/environment-variables API is now the single source
of truth for app env vars. The CLI's existing `app update-env` / `app list-env`
commands talk to the legacy Foundry-version env-var path, leaving CLI users
without a way to author env vars against the new platform store. This
introduces the `prisma app env` namespace to fill that gap.

New verbs:

- `prisma app env set KEY=VALUE` — create or replace a variable on a project
  template (`--class production` / `--class preview`) or a branch override
  (`--branch <name>`). Idempotent upsert: existing rows are replaced via PATCH,
  new rows are created via POST.
- `prisma app env list` — list metadata for a scope (no values, FR15). Defaults
  to `--class production` when no scope flag is provided.
- `prisma app env unset KEY` — looks the row up by natural key and DELETEs it.

Flag rules per spec FR21:

- `--class` and `--branch` are mutually exclusive.
- `set` and `unset` require an explicit scope so the CLI never silently writes
  to production.
- `--branch <name>` resolves to a Branch via `GET /v1/projects/{projectId}/branches?gitName=`.

Branch-override writes (`set`/`unset` against `--branch`) currently surface a
focused "feature unavailable" error: the POST/PATCH request bodies on
/v1/environment-variables don't accept a branchId yet. List works against
`--branch` because the GET endpoint already supports the filter. Branch-write
support lands in a follow-up.

Legacy `app update-env` / `app list-env` continue to work but print a
deprecation banner pointing at the new commands. The banner is suppressed
under --json so machine consumers keep their JSON channel clean. Removal is
deferred to a separate decision spike with the Terminal team.

The `@prisma/management-api-sdk` dep is bumped from ^1.24.0 to ^1.27.0 to pull
in the typed client for the new endpoints.

Tests cover: set happy path (POST), set replace (PATCH upsert), set with
both flags, set with neither flag, malformed KEY=VALUE, invalid key shape,
branch-override write feature-unavailable, list metadata-only contract,
list defaulting to production, list with branch resolution, unset happy
path, unset not-found, unset with neither flag, deprecation banner emitted
to stderr, deprecation banner suppressed under --json.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary by CodeRabbit

  • New Features

    • Added prisma app env command namespace for managing encrypted, platform-stored environment variables scoped to production, preview, or branch environments.
    • New subcommands: set (create/update), list (view), and unset (delete) with --class and --branch scoping options.
  • Deprecated

    • prisma app update-env and prisma app list-env now display deprecation warnings; use prisma app env set and prisma app env list instead.

Walkthrough

This PR introduces the prisma app env command namespace for managing durable platform-stored environment variables. It adds three subcommands (set, list, unset) with scope flags (--class or --branch), controllers for API integration, input validation, output formatting, and marks legacy update-env and list-env commands as deprecated while adding comprehensive tests.

Changes

App Environment Variables Management

Layer / File(s) Summary
Data Types and Schemas
packages/cli/src/types/app-env.ts
Introduces AppEnvScopeDescriptor, AppEnvVariableMetadata, and result interfaces (AppEnvSetResult, AppEnvListResult, AppEnvUnsetResult) for type-safe operation results.
Scope Resolution and Input Validation
packages/cli/src/lib/app/env-config.ts
Parses and validates --class/--branch scope flags, KEY=VALUE positional arguments, and enforces POSIX-style key constraints with max length.
Controller Implementation and API Integration
packages/cli/src/controllers/app-env.ts
Implements runAppEnvSet, runAppEnvList, and runAppEnvUnset controllers with project linking, API client creation, scope resolution, branch ID lookup, pagination, and error mapping.
Command Registration and Routing
packages/cli/src/commands/app/env.ts, packages/cli/src/commands/app/index.ts
Wires env, env set, env list, and env unset subcommands into the app CLI tree with argument/option definitions and action handlers.
Output Rendering and Serialization
packages/cli/src/presenters/app-env.ts
Formats controller results via renderAppEnv* functions for human display and serializeAppEnv* functions for JSON automation, omitting sensitive variable values.
Command Metadata and Legacy Deprecation
packages/cli/src/shell/command-meta.ts, packages/cli/src/controllers/app.ts
Registers command descriptors for the new env namespace and emits deprecation banners for legacy app update-env and app list-env commands.
Documentation, Tests, and Dependencies
docs/product/command-spec.md, packages/cli/package.json, packages/cli/tests/app-env.test.ts
Documents new command spec with scope flag rules and operation semantics, marks legacy commands deprecated, bumps @prisma/management-api-sdk to ^1.27.0, and adds test coverage for set/list/unset with input validation and branch scoping.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding the prisma app env set/list/unset namespace to the CLI.
Description check ✅ Passed The description comprehensively explains the PR's objectives, including new commands, scope rules, surface policy, legacy compatibility, and test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ptl-1475-add-prisma-app-env-setlistunset-to-prisma-cli
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch ptl-1475-add-prisma-app-env-setlistunset-to-prisma-cli

Comment @coderabbitai help to get the list of available commands and usage tips.

@kristof-siket
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 515-618: Add two tests mirroring the existing runAppUpdateEnv
cases but for runAppListEnv: one that calls runAppListEnv(context,
"hello-world") in human mode and asserts stderr.buffer contains "[deprecation]"
and the suggested guidance strings, and another that creates context with flags:
{ json: true } and asserts stderr.buffer does not contain "[deprecation]". In
the test setup reuse the same vi.doMock blocks but ensure the preview provider
mock returned by createPreviewAppProvider includes the method used by
runAppListEnv (e.g., listAppEnv or listEnvs) returning the same shape as
updateAppEnv/listDeployments so the controller resolves correctly; import
runAppListEnv from ../src/controllers/app and
createTempCwd/createTestCommandContext from ./helpers just like the update-env
tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c17f1916-8c6c-4f2e-910f-b864ceabd27d

📥 Commits

Reviewing files that changed from the base of the PR and between 9b08402 and cd09f79.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • docs/product/command-spec.md
  • packages/cli/package.json
  • packages/cli/src/commands/app/env.ts
  • packages/cli/src/commands/app/index.ts
  • packages/cli/src/controllers/app-env.ts
  • packages/cli/src/controllers/app.ts
  • packages/cli/src/lib/app/env-config.ts
  • packages/cli/src/presenters/app-env.ts
  • packages/cli/src/shell/command-meta.ts
  • packages/cli/src/types/app-env.ts
  • packages/cli/tests/app-env.test.ts

Comment thread packages/cli/tests/app-env.test.ts
Adds parity coverage for runAppListEnv mirroring the runAppUpdateEnv
deprecation tests: the banner is emitted to stderr in human mode and
suppressed under --json. Without this guard, a future change that
silently drops or alters the list-env deprecation policy would slip
through, since only update-env was previously pinned by tests.

Addresses CodeRabbit nitpick on PR #4.
@kristof-siket
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 281-435: Tests for mutual exclusion of scope flags are missing for
runAppEnvList and runAppEnvUnset; add negative tests that call
controllers.runAppEnvList and controllers.runAppEnvUnset with both className (or
--class) and branchName (or --branch) supplied and assert the command returns an
error (or throws) and that no HTTP client GET/DELETE is invoked. Specifically,
add one test for runAppEnvList that passes { className: "production",
branchName: "feature" } and expects a validation error and that client.GET was
not called, and another for runAppEnvUnset that passes both scope flags and
expects a validation error and that client.DELETE (or appropriate client method)
was not called; place these alongside the existing app env list tests so they
guard the shared validation rule.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0e61e4fa-4107-41b3-8c23-f566e08f80f7

📥 Commits

Reviewing files that changed from the base of the PR and between cd09f79 and 7ed4146.

📒 Files selected for processing (1)
  • packages/cli/tests/app-env.test.ts

Comment thread packages/cli/tests/app-env.test.ts
The mutex rule is enforced by a shared validator; pinning it on every
verb (set, list, unset) ensures a future refactor that splits the
validators (or short-circuits one path) can't regress just a single
entry point silently.

Addresses CodeRabbit nitpick on PR #4.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

@kristof-siket
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 571-748: The tests repeat the same
vi.doMock("../src/lib/app/preview-provider", ...) scaffold; extract that into a
reusable helper (e.g., export a setupPreviewProviderMock function from
tests/helpers.ts) that calls vi.doMock and returns or accepts overrides for
createPreviewAppProvider's methods (listApps, listDeployments, updateAppEnv,
listAppEnvNames, etc.), then replace each repeated vi.doMock block in
packages/cli/tests/app-env.test.ts with a single call to
setupPreviewProviderMock(...) (imported from helpers) passing only the method
overrides needed for that test; ensure the helper names the mocked factory
createPreviewAppProvider to match existing code and document that tests should
call/reset the mock between cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 979ad4ea-6ab8-4d5d-975b-e9b981eb5e07

📥 Commits

Reviewing files that changed from the base of the PR and between 7ed4146 and 44aba40.

📒 Files selected for processing (1)
  • packages/cli/tests/app-env.test.ts

Comment thread packages/cli/tests/app-env.test.ts Outdated
Centralises the auth/config/preview-provider mock setup that was
repeated in each of the four legacy-deprecation tests into a single
mockLegacyEnvDependencies helper plus two happy-path response
factories. A future change to the seam — e.g. extending the preview
provider's contract or moving the link lookup off readLinkedProjectId
— now needs to be reflected in exactly one place instead of fanning
out across every deprecation test.

Addresses CodeRabbit nitpick on PR #4.
@kristof-siket
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

1 similar comment
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/cli/tests/app-env.test.ts (1)

614-652: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider consolidating duplicate happy-path factories.

updateAppEnvHappyPath and listAppEnvNamesHappyPath return identical structures. Extracting a shared factory (e.g., createLegacyEnvResponse()) would eliminate duplication and simplify future response-shape changes.

♻️ Consolidation sketch
+const createLegacyEnvResponse = () => ({
+  projectId: "proj_123",
+  app: {
+    id: "app_1",
+    name: "hello-world",
+    region: null,
+    liveDeploymentId: "dep_1",
+    liveUrl: null,
+  },
+  deployment: {
+    id: "dep_1",
+    status: "running",
+    url: null,
+    createdAt: "2026-05-08T10:00:00.000Z",
+    live: true,
+  },
+  variables: ["FOO"],
+});
+
-const updateAppEnvHappyPath = () =>
-  vi.fn().mockResolvedValue({
-    projectId: "proj_123",
-    app: { ... },
-    deployment: { ... },
-    variables: ["FOO"],
-  });
+const updateAppEnvHappyPath = () =>
+  vi.fn().mockResolvedValue(createLegacyEnvResponse());

-const listAppEnvNamesHappyPath = () =>
-  vi.fn().mockResolvedValue({
-    projectId: "proj_123",
-    app: { ... },
-    deployment: { ... },
-    variables: ["FOO"],
-  });
+const listAppEnvNamesHappyPath = () =>
+  vi.fn().mockResolvedValue(createLegacyEnvResponse());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/tests/app-env.test.ts` around lines 614 - 652, Both
updateAppEnvHappyPath and listAppEnvNamesHappyPath return the same mock
response; extract the shared object into a single factory (e.g.,
createLegacyEnvResponse) and have updateAppEnvHappyPath and
listAppEnvNamesHappyPath call that factory to remove duplication. Update
references in tests to use the new factory where needed and ensure the returned
shape (projectId, app, deployment, variables) remains identical so existing
assertions keep working.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 5-10: The afterEach cleanup in the test does not explicitly unmock
the preview-provider module that mockLegacyEnvDependencies registers; add a
vi.doUnmock("../src/lib/app/preview-provider") call inside the existing
afterEach alongside the other vi.doUnmock(...) calls so the mock is removed from
Vitest's registry (ensure this is placed with the existing afterEach block that
currently calls vi.doUnmock("../src/adapters/config"),
vi.doUnmock("../src/lib/auth/guard"), vi.resetModules(), and
vi.restoreAllMocks()).

---

Duplicate comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 614-652: Both updateAppEnvHappyPath and listAppEnvNamesHappyPath
return the same mock response; extract the shared object into a single factory
(e.g., createLegacyEnvResponse) and have updateAppEnvHappyPath and
listAppEnvNamesHappyPath call that factory to remove duplication. Update
references in tests to use the new factory where needed and ensure the returned
shape (projectId, app, deployment, variables) remains identical so existing
assertions keep working.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3d2fe338-4e57-4fc1-b3f0-8e3279a750e5

📥 Commits

Reviewing files that changed from the base of the PR and between 44aba40 and c481a4e.

📒 Files selected for processing (1)
  • packages/cli/tests/app-env.test.ts

Comment thread packages/cli/tests/app-env.test.ts
Pulls a single legacyEnvProviderResponse() factory out of the two
happy-path mock builders that were returning identical shapes, and
adds vi.doUnmock("../src/lib/app/preview-provider") to afterEach
so a stale registration can't bleed across sibling test files.
Both addresses CodeRabbit nitpicks on PR #4.
@kristof-siket
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

1 similar comment
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 463-558: Add a test that mirrors the existing "set --branch
unavailable" case to assert that runAppEnvUnset rejects when passed branchName
(branch-override writes are unavailable): create a test using createMockClient +
loadControllers + writePrismaConfig + createTestCommandContext, call
controllers.runAppEnvUnset(context, "STRIPE_KEY", { branchName: "feature-auth"
}) and expect it to reject with a feature-unavailable style error (e.g.,
toMatchObject({ code: "FEATURE_UNAVAILABLE" }) or summary containing
"branch-override" or "branch"), and assert client.DELETE was not called;
reference controllers.runAppEnvUnset, createMockClient, loadControllers, and
client.DELETE so the test sits alongside the other unset validation specs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6862c154-37f8-42b4-8858-54d3c2daea76

📥 Commits

Reviewing files that changed from the base of the PR and between c481a4e and 11daaea.

📒 Files selected for processing (1)
  • packages/cli/tests/app-env.test.ts

Comment on lines +463 to +558
describe("app env unset", () => {
it("looks up the row and DELETEs it on the happy path", async () => {
const client = createMockClient();
client.GET.mockResolvedValueOnce({
data: {
data: [makeVariableRow({ id: "envvar_target", key: "STRIPE_KEY" })],
pagination: { hasMore: false, nextCursor: null },
},
response: { status: 200 },
});
client.DELETE.mockResolvedValueOnce({
data: undefined,
response: { status: 204 },
});

const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

const result = await controllers.runAppEnvUnset(context, "STRIPE_KEY", {
className: "production",
});

expect(client.DELETE).toHaveBeenCalledWith(
"/v1/environment-variables/{envVarId}",
expect.objectContaining({
params: { path: { envVarId: "envvar_target" } },
}),
);
expect(result.result).toEqual({
projectId: "proj_123",
scope: { kind: "class", class: "production" },
key: "STRIPE_KEY",
});
});

it("returns a focused not-found error when the row does not exist", async () => {
const client = createMockClient();
client.GET.mockResolvedValueOnce({
data: { data: [], pagination: { hasMore: false, nextCursor: null } },
response: { status: 200 },
});

const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

await expect(
controllers.runAppEnvUnset(context, "STRIPE_KEY", {
className: "production",
}),
).rejects.toMatchObject({
code: "ENV_VARIABLE_NOT_FOUND",
});
expect(client.DELETE).not.toHaveBeenCalled();
});

it("rejects neither --class nor --branch (fail-fast on writes)", async () => {
const client = createMockClient();
const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

await expect(
controllers.runAppEnvUnset(context, "STRIPE_KEY", {}),
).rejects.toMatchObject({
summary: expect.stringContaining("requires --class or --branch"),
});
expect(client.DELETE).not.toHaveBeenCalled();
});

it("rejects --class and --branch supplied together", async () => {
const client = createMockClient();
const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

await expect(
controllers.runAppEnvUnset(context, "STRIPE_KEY", {
className: "production",
branchName: "feature-auth",
}),
).rejects.toMatchObject({
summary: expect.stringContaining("mutually exclusive"),
});
expect(client.DELETE).not.toHaveBeenCalled();
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add explicit unset --branch feature-unavailable test.

Line 463 onward covers unset validation, but it doesn’t pin the documented “branch-override writes unavailable” behavior for runAppEnvUnset. Add one test mirroring the set --branch unavailable case to prevent regressions.

Suggested test addition
 describe("app env unset", () => {
+  it("returns a feature-unavailable error for branch-override writes", async () => {
+    const client = createMockClient();
+    const { controllers, createTempCwd, createTestCommandContext } =
+      await loadControllers(client, "proj_123");
+    const cwd = await createTempCwd();
+    await writePrismaConfig(cwd, "proj_123");
+    const { context } = await createTestCommandContext({ cwd });
+
+    await expect(
+      controllers.runAppEnvUnset(context, "STRIPE_KEY", {
+        branchName: "feature-auth",
+      }),
+    ).rejects.toMatchObject({
+      summary: expect.stringContaining("Branch-override writes are not available yet"),
+    });
+    expect(client.DELETE).not.toHaveBeenCalled();
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/tests/app-env.test.ts` around lines 463 - 558, Add a test that
mirrors the existing "set --branch unavailable" case to assert that
runAppEnvUnset rejects when passed branchName (branch-override writes are
unavailable): create a test using createMockClient + loadControllers +
writePrismaConfig + createTestCommandContext, call
controllers.runAppEnvUnset(context, "STRIPE_KEY", { branchName: "feature-auth"
}) and expect it to reject with a feature-unavailable style error (e.g.,
toMatchObject({ code: "FEATURE_UNAVAILABLE" }) or summary containing
"branch-override" or "branch"), and assert client.DELETE was not called;
reference controllers.runAppEnvUnset, createMockClient, loadControllers, and
client.DELETE so the test sits alongside the other unset validation specs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Documents the asymmetry between set and unset on branch scopes:
`unset --branch` works against an existing override row because
DELETE /v1/environment-variables/{envVarId} addresses the row by id
and doesn't depend on the POST/PATCH body extension that gates the
branch-override write path. The new test guards against a
sympathetic-but-wrong refactor that would route unset --branch
through the same feature-unavailable gate as set --branch.
@kristof-siket
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

1 similar comment
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/tests/app-env.test.ts`:
- Around line 165-215: These tests assert only specific client methods (e.g.,
expect(client.POST).not.toHaveBeenCalled()) which can miss leaked calls to other
HTTP methods; update each negative-path test that uses createMockClient() and
controllers.runAppEnvSet (and the similar tests later) to assert that all client
methods on the mock (at minimum client.GET, client.POST, client.PATCH,
client.DELETE) were not called so the tests fail if any stray API call is made.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02e7df2c-b36f-4c6c-9abe-9263c93b8ecc

📥 Commits

Reviewing files that changed from the base of the PR and between c481a4e and bb48418.

📒 Files selected for processing (1)
  • packages/cli/tests/app-env.test.ts

Comment on lines +165 to +215
it("rejects --class and --branch supplied together", async () => {
const client = createMockClient();
const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

await expect(
controllers.runAppEnvSet(context, "STRIPE_KEY=sk", {
className: "production",
branchName: "feature-auth",
}),
).rejects.toMatchObject({
summary: expect.stringContaining("mutually exclusive"),
});
expect(client.POST).not.toHaveBeenCalled();
});

it("rejects neither --class nor --branch (fail-fast on writes)", async () => {
const client = createMockClient();
const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

await expect(
controllers.runAppEnvSet(context, "STRIPE_KEY=sk", {}),
).rejects.toMatchObject({
summary: expect.stringContaining("requires --class or --branch"),
});
expect(client.POST).not.toHaveBeenCalled();
});

it("rejects malformed KEY=VALUE", async () => {
const client = createMockClient();
const { controllers, createTempCwd, createTestCommandContext } =
await loadControllers(client, "proj_123");
const cwd = await createTempCwd();
await writePrismaConfig(cwd, "proj_123");
const { context } = await createTestCommandContext({ cwd });

await expect(
controllers.runAppEnvSet(context, "noequalshere", {
className: "production",
}),
).rejects.toMatchObject({
summary: expect.stringContaining("missing the = separator"),
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Strengthen fail-fast tests by asserting zero API calls across all methods.

These negative-path tests currently assert only a subset of client methods (POST or DELETE), so regressions could still leak GET/PATCH calls without failing the suite.

Proposed test-hardening diff
+function expectNoApiCalls(client: MockClient) {
+  expect(client.GET).not.toHaveBeenCalled();
+  expect(client.POST).not.toHaveBeenCalled();
+  expect(client.PATCH).not.toHaveBeenCalled();
+  expect(client.DELETE).not.toHaveBeenCalled();
+}
+
 describe("app env set", () => {
   it("rejects --class and --branch supplied together", async () => {
@@
-    expect(client.POST).not.toHaveBeenCalled();
+    expectNoApiCalls(client);
   });

   it("rejects neither --class nor --branch (fail-fast on writes)", async () => {
@@
-    expect(client.POST).not.toHaveBeenCalled();
+    expectNoApiCalls(client);
   });

   it("rejects malformed KEY=VALUE", async () => {
@@
     await expect(
       controllers.runAppEnvSet(context, "noequalshere", {
         className: "production",
       }),
     ).rejects.toMatchObject({
       summary: expect.stringContaining("missing the = separator"),
     });
+    expectNoApiCalls(client);
   });

   it("rejects keys that don't match POSIX env-var shape", async () => {
@@
     await expect(
       controllers.runAppEnvSet(context, "lowercase-key=value", {
         className: "production",
       }),
     ).rejects.toMatchObject({
       summary: expect.stringContaining("POSIX env-var shape"),
     });
+    expectNoApiCalls(client);
   });
 });
@@
 describe("app env unset", () => {
   it("rejects neither --class nor --branch (fail-fast on writes)", async () => {
@@
-    expect(client.DELETE).not.toHaveBeenCalled();
+    expectNoApiCalls(client);
   });

   it("rejects --class and --branch supplied together", async () => {
@@
-    expect(client.DELETE).not.toHaveBeenCalled();
+    expectNoApiCalls(client);
   });
 });

Also applies to: 524-557

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/tests/app-env.test.ts` around lines 165 - 215, These tests
assert only specific client methods (e.g.,
expect(client.POST).not.toHaveBeenCalled()) which can miss leaked calls to other
HTTP methods; update each negative-path test that uses createMockClient() and
controllers.runAppEnvSet (and the similar tests later) to assert that all client
methods on the mock (at minimum client.GET, client.POST, client.PATCH,
client.DELETE) were not called so the tests fail if any stray API call is made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant