Skip to content

feat(stack): allow repeatable --stack-id in stack destroy (#6964) - #7000

Open
tirthraj01 wants to merge 2 commits into
supabase:developfrom
tirthraj01:feat/stack-destroy-multiple-ids
Open

tirthraj01 wants to merge 2 commits into
supabase:developfrom
tirthraj01:feat/stack-destroy-multiple-ids

Conversation

@tirthraj01

Copy link
Copy Markdown

What kind of change does this PR introduce?

Feature

What is the current behavior?

supabase stack destroy only accepts a single --stack-id string argument. Cleaning up multiple temporary or local stacks requires executing stack destroy multiple times, prompting for confirmation on each run.

Fixes #6964

What is the new behavior?

  • Repeatable --stack-id: The --stack-id flag now accepts multiple values (e.g. supabase stack destroy --stack-id id1 --stack-id id2 --yes).
  • Mutual Exclusivity: --stack and --stack-id remain strictly mutually exclusive when single or multiple IDs are provided.
  • Single Confirmation Prompt: Resolves and deduplicates all targets upfront before prompting once listing all target IDs. If any stack ID is invalid or not found, it fails upfront before prompting or destroying anything.
  • Resilient Batch Deletion: Iterates over resolved targets; if one stack fails to destroy, the failure is reported, the remaining stacks continue being destroyed, and the command exits with non-zero status at the end.
  • Added comprehensive unit and integration tests covering multi-id destruction, batch confirmation prompt cancellation, --stack / --stack-id mutual exclusivity, and resilient loop error handling.

Additional context

Tested against integration test fixtures:

  • bun --bun vitest run apps/cli/src/commands/experimental/stack/destroy/destroy.integration.test.ts apps/cli/src/commands/experimental/stack/stack.shared.integration.test.ts (25 passed)
  • Static analysis & linting: types:check, lint:check, fmt:check, and lint:effect:check all pass with 0 errors.

@tirthraj01
tirthraj01 requested a review from a team as a code owner October 5, 2026 19:46
Comment thread apps/cli/src/commands/experimental/stack/destroy/destroy.handler.ts

@7ttp 7ttp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

super, thanks for this! 💚 a few things to address before this lands:

Comment on lines +236 to +241
yield* output.success("", {
destroyed: failures.length === 0,
stacks: successes.map(({ id, result }) => ({ id, ...result })),
failures: failures.map(({ id, error }) => ({ id, message: error.message })),
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could this emit one error envelope? failed destroys currently print two JSON documents MachineErrorContext could carry the partial results, as stack start does here?:

const machineErrorContext = yield* Effect.serviceOption(MachineErrorContext);
if (Option.isSome(machineErrorContext))
yield* machineErrorContext.value.set({
stack_changes: rejected.changes,
recreate_command: rejected.command,
});
return yield* rejected.error;

Comment on lines +198 to +201
yield* stackDestroy({
...f.flags,
stackId: [f.stack.id, second.id],
}).pipe(Effect.provide(f.layer));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this passes an array directly, lets run it thru Command.runWith it would cover repeated flags too

tbh would also be worth covering a missing later ID and prefix/full-ID dedupe...

Flag.withDescription("Destroy an existing stack by id or unique id prefix."),
Flag.optional,
Flag.withDescription(
"Destroy an existing stack by id or unique id prefix; repeat to select several.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could you also update stack-commands.md and SIDE_EFFECTS.md ?

Comment on lines +201 to +208
const error = Option.getOrElse(
Exit.findErrorOption(exit),
() =>
new StackCommandDestroyError({
reason: "unknown",
message: `Failed to destroy stack ${targetId}`,
}),
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ig we should we keep the original error here? instead of a generic failure

}),
);
failures.push({ id: targetId, error });
yield* output.error(`Failed to destroy stack ${targetId}: ${error.message}`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

imo the task and final error already report this failure. so we prob can drop this extra error line on stdout as well..

This branch has not been deployed

No deployments
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.

stack: stack destroy should accept a list of stack ids

2 participants