run method

  1. @override
Future<void> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<void> run() async {
  buildStartTime = DateTime.now();

  // 1. Initialize Services
  config = ConfigService(projectDir);
  whiteLabel = WhiteLabelService(projectDir: projectDir, config: config);
  cleanup = CleanupService(projectDir: projectDir, config: config);

  final client = argResults!['client'] as String;
  final platform = argResults!['platform'] as String;
  final type = argResults!['type'] as String;
  final isTest = argResults!['test'] as bool;
  final enableSlack = argResults!['slack'] as bool;

  String? version;
  String? errorMessage;
  bool buildSuccess = false;

  if (enableSlack) {
    slackService =
        await _initializeSlackService(argResults!['slack-channel']);
  }
  try {
    // PHASE 1: VALIDATION & SETUP
    print('--- โš™๏ธ Phase 1: Validation & Setup ---');
    version = await config.getPubspecVersion();
    await _notifyBuildStep('Build Started', client, platform, 'started',
        additionalInfo: 'Version: $version, Type: $type');

    final envVars = await config.parseEnvFile(
        File('$projectDir/clients/$client/${isTest ? '.env_test' : '.env'}'));

    await config.copyToRootEnv(
        File('$projectDir/clients/$client/${isTest ? '.env_test' : '.env'}'));

    final appName = envVars['APP_NAME_PROD']!;
    final bundleId = envVars['BUNDLE_ID']!;
    final clientAssetsPath = envVars['ASSETS_PATH']!;
    final appIconPath = envVars['APP_ICON_PATH']!;
    appNameForCleanup = appName;

    // PHASE 2: PROJECT CONFIGURATION
    print('\n--- โœ๏ธ Phase 2: Project Configuration ---');
    await _notifyBuildStep('Project Configuration', client, '', 'started');

    await config.createBackup(File('$projectDir/pubspec.yaml'));
    await config
        .createBackup(File('$projectDir/flutter_launcher_icons.yaml'));

    await config.updateYamlValue(
        File('$projectDir/flutter_launcher_icons.yaml'),
        ['flutter_launcher_icons', 'image_path'],
        appIconPath);

    print(
        'Updated `flutter_launcher_icons.yaml` image_path to `$appIconPath`');

    await config.updateYamlValue(File('$projectDir/pubspec.yaml'),
        ['splash_master', 'image'], appIconPath);

    print('Updated `pubspec.yaml` splash_master image to `$appIconPath`');
    await whiteLabel.syncBrandingAssets(client, clientAssetsPath);
    await whiteLabel.applyClientFonts(clientAssetsPath);

    // Update YAMLs via Config Service
    await config.updatePubspecAssets(
        clientAssetPath: client, requiredExtraAssets: ['.env']);

    // PHASE 3: RUNNING EXTERNAL TOOLS
    print('\n--- ๐Ÿš€ Phase 3: Running Build Commands ---');
    await runShell('flutter pub get');
    await runShell(
        'dart run rename setBundleId --targets ios,android --value "$bundleId"');
    await runShell(
        'dart run rename setAppName --targets ios,android --value "$appName"');
    await runShell('dart run flutter_launcher_icons');
    await runShell('dart run splash_master create');

    await whiteLabel.patchIosSplash(appName);
    await whiteLabel.cleanAndroidIconCache();

    // PHASE 4: THE FINAL BUILD
    print('\n--- ๐Ÿ“ฆ Phase 4: Building the App ---');
    if (platform == 'android') {
      final buildType = (type == 'aab') ? 'aab' : 'apk --release';
      await runShell(
          'flutter build $buildType --dart-define=CLIENT_ENV=".env"');

      builtFile = await whiteLabel.renameOutput(
          clientName: client, version: version, type: type);
    } else {
      await runShell('flutter build ipa --dart-define=CLIENT_ENV=".env"');
    }

    buildSuccess = true;
    print('\nโœ…โœ…โœ… Build process completed successfully! โœ…โœ…โœ…');
  } catch (e) {
    buildSuccess = false;
    errorMessage = e.toString();
    await _notifyBuildStep('Build Process', client, platform, 'failed',
        errorMessage: errorMessage);
    rethrow; // Ensure cleanup runs but user sees the error
  } finally {
    // FINAL PHASE: CLEANUP (ALWAYS RUNS)

    print('\n--- ๐Ÿงน Final Phase: Cleaning Up ---');

    await cleanup.performFullCleanup(
      appNameForCleanup: appNameForCleanup,
      fontsWereChanged: true,
    );
    if (slackService != null && version != null) {
      final buildDuration = DateTime.now().difference(buildStartTime!);
      await slackService!.sendBuildSummary(
        client: client,
        platform: platform,
        type: type,
        version: version,
        success: buildSuccess,
        buildTime: buildDuration,
        errorMessage: errorMessage,
        artifactFile: builtApkFile,
      );
    }
    buildSuccess = false;
  }
}