replaceFlavor method

Future<void> replaceFlavor({
  1. required String oldFlavor,
  2. required String newFlavor,
})

Implementation

Future<void> replaceFlavor({
  required String oldFlavor,
  required String newFlavor,
}) async {
  final root = ConfigService.root;
  final tempDir = Directory.systemTemp.createTempSync('flavor_cli_backup_');
  _log.info('📸 Creating pre-flight snapshot...');

  try {
    // 1. SNAPSHOT
    final pathsToBackup = [
      'flavor_cli.yaml',
      'ios/Runner.xcodeproj',
      'ios/Flutter',
      'android/app/build.gradle',
      'android/app/build.gradle.kts',
      'android/app/src/main/AndroidManifest.xml',
      'lib',
      'test/widget_test.dart',
      'firebase.json',
    ];

    // Include .env files in backup
    final config = ConfigService.load();
    for (final flavor in config.flavors) {
      pathsToBackup.add('.env.$flavor');
    }
    pathsToBackup.add('.env.$oldFlavor'); // ensure old file is backed up

    for (final relativePath in pathsToBackup) {
      final src = FileSystemEntity.isDirectorySync(p.join(root, relativePath))
          ? Directory(p.join(root, relativePath))
          : File(p.join(root, relativePath));

      if (!src.existsSync()) continue;

      final destPath = p.join(tempDir.path, relativePath);
      if (src is Directory) {
        _copyDirectory(src, Directory(destPath));
      } else if (src is File) {
        File(destPath).createSync(recursive: true);
        src.copySync(destPath);
      }
    }

    // 2. ATTEMPT RENAME SEQUENCE
    final isProduction = oldFlavor == config.productionFlavor;

    ConfigService.renameFlavor(oldFlavor, newFlavor);
    if (isProduction) {
      ConfigService.save(
        ConfigService.load().copyWith(productionFlavor: newFlavor),
      );
    }

    final updatedConfig = ConfigService.load();
    final adapter = RuntimeConfigService();

    // Rename adapter-specific files (e.g., .env.<old> -> .env.<new>)
    adapter.renameFlavorFiles(oldFlavor, newFlavor, updatedConfig);
    adapter.generateAppConfig(updatedConfig);

    FileService.renameFlavor(
      oldName: oldFlavor,
      newName: newFlavor,
      log: _log,
    );

    if (updatedConfig.platforms.contains('android')) {
      AndroidService.setupFlavors(config: updatedConfig, logger: _log);
    }
    if (updatedConfig.platforms.contains('ios')) {
      IOSService.setupSchemes(config: updatedConfig, logger: _log);
    }
    if (updatedConfig.platforms.contains('macos')) {
      MacOSService.setupSchemes(config: updatedConfig, logger: _log);
    }
    if (updatedConfig.platforms.contains('web')) {
      WebService.setupFlavors(updatedConfig, _log);
    }

    FileService.updateTests();

    final orphans = FileService.getOrphanedFlavors(updatedConfig.flavors);
    if (orphans.isNotEmpty) {
      FileService.cleanupFlavors(orphans.toList());
      adapter.cleanupFlavorFiles(orphans.toList(), updatedConfig);
      for (final orphan in orphans) {
        _safe(
          () => IOSService.removeFlavorSchemes(orphan, logger: _log),
          'iOS cleanup for $orphan',
        );
        _safe(
          () => MacOSService.removeFlavorSchemes(orphan, logger: _log),
          'macOS cleanup for $orphan',
        );
      }
    }

    FileService.updateVSCodeLaunchConfig();
    FileService.injectFirebase(separate: updatedConfig.useSeparateMains);

    // Clean up temp dir on success
    tempDir.deleteSync(recursive: true);
    _log.success(
      '✅ Flavor "$oldFlavor" successfully renamed to "$newFlavor".',
    );

    await FirebaseCommand.checkAndReinit(_log, targetFlavor: newFlavor);
  } catch (e) {
    _log.error('❌ Rename failed: $e');
    _log.info(
      '🔄 Initiating automatic rollback to preserve project integrity...',
    );

    // 3. ROLLBACK FROM SNAPSHOT
    for (final entity in tempDir.listSync(recursive: true)) {
      if (entity is File) {
        final relativePath = p.relative(entity.path, from: tempDir.path);
        final targetFile = File(p.join(root, relativePath));
        targetFile.createSync(recursive: true);
        entity.copySync(targetFile.path);
      }
    }

    try {
      tempDir.deleteSync(recursive: true);
    } catch (_) {}

    _log.success(
      '✔ Rollback successful. Project restored to "$oldFlavor" state.',
    );
    rethrow;
  }
}