Skip to content

feat: Interactive confirmation prompts for destructive operations #33

@Lazialize

Description

@Lazialize

Problem

When strata apply or strata generate produce migrations containing destructive changes (table drops, column drops, type changes, etc.), the only control mechanism is the --allow-destructive flag. Without the flag, execution stops with an error. But when the flag is provided, everything executes without any confirmation. An interactive confirmation step is missing to prevent accidental operations in production.

Proposed Solution

Display an interactive confirmation prompt before executing operations that include destructive changes. In CI/CD environments, --yes / --force flags allow skipping the prompt.

Confirmation Flow

⚠️  Destructive Changes Detected
─────────────────────────────────
The following destructive operations will be performed:

  🗑️  DROP TABLE: legacy_users
  🗑️  DROP COLUMN: users.old_email
  ♻️  RECREATE ENUM: user_status (values changed)

These operations are IRREVERSIBLE and may cause data loss.

Do you want to proceed? [y/N]: 

Implementation Plan

  1. Confirmation prompt module (src/cli/src/cli/commands/prompt.rs — new)

    pub struct ConfirmationPrompt {
        pub title: String,
        pub items: Vec<DestructiveItem>,
        pub default_no: bool,  // Default is deny
    }
    
    impl ConfirmationPrompt {
        pub fn confirm(&self) -> Result<bool>;
    }
  2. CLI flag additions

    • --yes / -y: Skip confirmation (for CI/CD)
    • Combine with existing --allow-destructive
  3. Changes to apply command (src/cli/src/cli/commands/apply.rs)

    • Show prompt when DestructiveChangeDetector result is non-empty
    • Skip prompt when --yes flag is present
    • Require --yes when stdin is not a TTY (pipe input)
  4. Changes to generate command (src/cli/src/cli/commands/generate.rs)

    • Show prompt when generating SQL with destructive changes
  5. Interactive init command (src/cli/src/cli/commands/init.rs)

    • Interactive dialect selection and directory name input
    • --yes uses all default values

TTY Detection

use std::io::IsTerminal;

fn should_prompt(yes_flag: bool) -> bool {
    !yes_flag && std::io::stdin().is_terminal()
}

Files Affected

  • src/cli/src/cli/commands/prompt.rs (new)
  • src/cli/src/cli/commands/apply.rs — Add confirmation flow
  • src/cli/src/cli/commands/generate.rs — Add confirmation flow
  • src/cli/src/cli/commands/init.rs — Interactive initialization
  • src/cli/src/cli/cli.rs--yes global argument

Alternatives Considered

  • --force flag only: A flag-only approach still risks accidental misuse
  • Mandatory --dry-run first: Requiring --dry-run before destructive changes makes the workflow cumbersome

Additional Context

  • Corresponds to "Require explicit flags for destructive operations" and "Add interactive prompts for initialization and dangerous operations" in ROADMAP.md
  • DestructiveChangeDetector and DestructiveChangeReport are already implemented, making detection result reuse straightforward
  • std::io::IsTerminal is stabilized since Rust 1.70+

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions