feat(cli): add --branch to app remove to scope teardown to one branch#123
Conversation
app remove destroys every deployment in the resolved branch, but it had
no --branch flag: it inferred the branch from the local Git branch and
fell back to the production branch ("main") when it couldn't read one.
CI teardown that faked a .git/HEAD to convey a PR branch could therefore
silently resolve to production and tear down the live app.
Add an explicit --branch <name> option (matching app deploy) so callers
scope the removal to a specific branch. Without --branch, existing
inferred resolution is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 31 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
WalkthroughThe 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/src/controllers/app.ts`:
- Around line 2058-2060: Update the branch selection near resolveDeployBranch to
distinguish an omitted branchName (undefined) from an explicitly supplied empty
string. Reject an empty branch value with the project’s standard validation
error before resolving the branch, while preserving resolution for valid names
and undefined behavior for omitted input.
In `@packages/cli/tests/app-controller.test.ts`:
- Around line 6764-6825: Add a command-level test covering createRemoveCommand
that parses “app remove --branch pr-42” and verifies the invoked controller
receives “pr-42” as the branch argument. Keep the existing runAppRemove test for
provider scoping, but ensure the new test exercises CLI option registration and
forwarding rather than calling the controller directly.
🪄 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: fe5dbf4f-6aec-4670-8456-17c692f0ad55
📒 Files selected for processing (3)
packages/cli/src/commands/app/index.tspackages/cli/src/controllers/app.tspackages/cli/tests/app-controller.test.ts
Address CodeRabbit review on the app remove --branch change: - Reject an explicitly supplied empty --branch (`--branch ""`) with a usage error instead of treating it as omitted. Previously the truthiness check let a blank value fall through to inferred Git-branch resolution — which can resolve to production — defeating the point of scoping the removal. - Distinguish undefined (omitted) from "" so omitting --branch keeps the existing inferred behavior. - Add command-level coverage: assert `app remove` registers --branch (guards against the option/forwarding being dropped) and a controller test that the empty value is rejected before any provider call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@CodeRabbit review |
✅ Action performedReview finished.
|
…mmand CodeRabbit follow-up: the registration guard plus the controller test still left a gap — a regression where --branch stays registered but options.branch is no longer passed to runAppRemove would pass both. Add a command-execution test that parses `app remove --branch pr-42` via createAppCommand and asserts the provider lookup is scoped to that branch, exercising commander parsing, the action handler's forwarding, and the controller's branch scoping in one path. Verified by mutation: dropping branchName from the action call makes the provider receive branchName "main" (the inferred production fallback) instead of "pr-42", and the test fails. Mocks the app provider rather than the controllers module — mocking controllers/app leaked past vi.resetModules and broke six later tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comply with the house rule that a comment needing more than one line means the code (or its API doc) is what's unclear. The branch-scoping rationale now lives in runAppRemove's JSDoc, the empty-branch guard keeps a single-line note, and the test drops a comment its name already covers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update the command spec signature, purpose, and behavior for `app remove` to cover branch scoping, and surface the flag in the in-CLI help examples. The description now says "resolved branch" rather than "current branch", since the target branch can be chosen. The branch-resolution section already covered management commands accepting `--branch`, so it needed no change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@CodeRabbit approve |
✅ Action performedComments resolved and changes approved. |
Why
app removedestroys every deployment in the resolved branch. But it had no--branchflag — it inferred the branch from the local Git branch and, when it couldn't read one, fell back to the production branch (main):A PR-teardown CI job that fakes a
.git/HEADto convey the PR branch (because there was no other lever) could therefore silently resolve to production and tear down the live app. This is exactly the mechanism behind a production incident on 2026-07-20 whereapp remove --app build-runner/--app github-webhookstopped the live production deployments.What
--branch <name>toapp remove, mirroringapp deploy. Callers scope the removal to a specific branch instead of relying on implicit Git resolution.--branch, the existing inferred resolution is unchanged (no behavior change for current users).The consuming CI workflow (
pdp-control-planepr-closed.yaml) will pass--branch "$PR_BRANCH"explicitly, so teardown can never resolve to production.Follow-up (not in this PR)
Consider hardening
app removeitself to refuse an inferred (non---branch) production target in non-interactive mode, so the silentmainfallback can't tear down production even without a--branch-aware caller. Left out here to avoid changing existing no---branchbehavior/tests.Tests
remove scopes the app lookup to an explicit --branch(assertslistAppsis scoped to the passed branch).app-controllersuite green (103 passed).🤖 Generated with Claude Code