run method

Future<void> run(
  1. FlavorConfig config, {
  2. String? newFlavor,
  3. bool skipFirebase = false,
})

Implementation

Future<void> run(
  FlavorConfig config, {
  String? newFlavor,
  bool skipFirebase = false,
}) async {
  try {
    final adapter = RuntimeConfigService();

    // 1. Save Config as the source of truth
    ConfigService.save(config);
    _log.info('📦 Running setup with flavors: ${config.flavors.join(", ")}');

    // 2. File Structure
    FileService.createStructure();

    // Generate AppConfig via adapter (JSON or ENV variant)
    adapter.generateAppConfig(config);

    // Generate flavor-specific runtime files (no-op for JSON, .env files for ENV)
    adapter.generateFlavorFiles(config);

    // Ensure dependencies and assets are configured in pubspec.yaml
    _ensureDependencies(config);

    FileService.updateTests();

    String? oldMainContent;
    if (config.useSeparateMains) {
      final mainFile = File(p.join(ConfigService.root, 'lib/main.dart'));
      if (mainFile.existsSync()) {
        oldMainContent = mainFile.readAsStringSync();
        _log.info(
          '📝 Preserving your existing main.dart content into the production flavor...',
        );
      }
    }

    bool overwriteMains = true;
    final existingMains = _checkExistingMains(
      config.flavors,
      config.useSeparateMains,
    );

    if (existingMains.isNotEmpty) {
      overwriteMains = false;
    }

    // Create main files using adapter-generated boilerplate
    _createMainFiles(
      config,
      adapter,
      overwrite: overwriteMains,
      productionContent: oldMainContent,
    );

    // If we are not in full overwrite mode OR we preserved old content,
    // integrate the files to ensure they have AppConfig.init and proper imports.
    if (!overwriteMains || oldMainContent != null) {
      if (config.useSeparateMains) {
        for (final flavor in config.flavors) {
          final path = p.join(
            ConfigService.root,
            'lib/main/main_$flavor.dart',
          );
          adapter.integrateMainFile(path, config, flavor: flavor);
        }
      } else {
        final path = p.join(ConfigService.root, 'lib/main.dart');
        adapter.integrateMainFile(path, config);
      }
    }

    // 3. Platform Setup
    if (config.platforms.contains('android')) {
      _safe(
        () => AndroidService.setupFlavors(config: config, logger: _log),
        'Android setup',
      );
    }
    if (config.platforms.contains('ios')) {
      _safe(
        () => IOSService.setupSchemes(config: config, logger: _log),
        'iOS setup',
      );
    }
    if (config.platforms.contains('macos')) {
      _safe(
        () => MacOSService.setupSchemes(config: config, logger: _log),
        'macOS setup',
      );
    }
    if (config.platforms.contains('web')) {
      _safe(() => WebService.setupFlavors(config, _log), 'Web setup');
    }

    // 4. Cleanup
    final orphans = FileService.getOrphanedFlavors(config.flavors);
    if (orphans.isNotEmpty) {
      FileService.cleanupFlavors(orphans.toList());
      // Also clean up adapter-specific orphan files
      adapter.cleanupFlavorFiles(orphans.toList(), config);
      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',
        );
      }
      _log.info('✔ Orphaned files cleaned up (${orphans.join(", ")})');
    }

    // Cleanup main files based on strategy
    if (config.useSeparateMains) {
      final rootMain = File(p.join(ConfigService.root, 'lib/main.dart'));
      if (rootMain.existsSync()) {
        rootMain.deleteSync();
      }
    } else {
      final mainDir = Directory(p.join(ConfigService.root, 'lib/main'));
      if (mainDir.existsSync()) {
        mainDir.deleteSync(recursive: true);
      }
    }

    _log.success('✅ Flavor system synchronized successfully!');

    FileService.updateVSCodeLaunchConfig();

    // Ensure Firebase entry points are synchronized (silently)
    if (ConfigService.hasFirebaseFiles()) {
      FileService.injectFirebase(separate: config.useSeparateMains);
    }

    // Check and re-initialize Firebase if necessary
    if (!skipFirebase) {
      await FirebaseCommand.checkAndReinit(_log, targetFlavor: newFlavor);
    }

    FileService
        .updateVSCodeLaunchConfig(); // Call again after Firebase just in case
  } catch (e) {
    if (e is CliException && e.isLogged) {
      rethrow;
    }
    _log.error('❌ Failed to synchronize flavors: $e');
    rethrow;
  }
}