runWithRetry method

Future<ProcessResult?> runWithRetry(
  1. String executable,
  2. List<String> arguments, {
  3. String? workingDirectory,
  4. Map<String, String>? environment,
  5. String? operationName,
  6. bool interactive = true,
})

Run a command with automatic retry on failure Returns null if user chooses to skip Throws AbortException if user chooses to abort

Implementation

Future<ProcessResult?> runWithRetry(
  String executable,
  List<String> arguments, {
  String? workingDirectory,
  Map<String, String>? environment,
  String? operationName,
  bool interactive = true,
}) async {
  final String opName = operationName ?? '$executable ${arguments.join(' ')}';
  int attempt = 0;

  while (true) {
    attempt++;
    final ProcessResult result = await run(
      executable,
      arguments,
      workingDirectory: workingDirectory,
      environment: environment,
    );

    if (result.success) {
      return result;
    }

    // Failed - check if we should auto-retry
    if (attempt <= maxAutoRetries) {
      warn('$opName failed (attempt $attempt/$maxAutoRetries), retrying...');
      await Future.delayed(const Duration(seconds: 1));
      continue;
    }

    // Auto-retries exhausted - ask user
    if (!interactive) {
      error('$opName failed after $maxAutoRetries attempts');
      error('stderr: ${result.stderr}');
      return null;
    }

    error('$opName failed after $maxAutoRetries attempts');
    if (result.stderr.isNotEmpty) {
      error('Error output:');
      print(result.stderr);
    }

    final RetryChoice choice = await UserPrompt.askRetryChoice(opName);
    switch (choice) {
      case RetryChoice.retry:
        attempt = 0; // Reset retry counter
        continue;
      case RetryChoice.skip:
        return null;
      case RetryChoice.abort:
        throw AbortException();
    }
  }
}