uploadApps function

Future<void> uploadApps(
  1. String clientId, {
  2. bool skipAll = false,
  3. bool skipAndroidUploadCheck = false,
  4. bool skipIOSUploadCheck = false,
})

Orchestrates the process of uploading Flutter applications to app stores using Fastlane.

This function reads the client's configuration and pubspec.yaml to get necessary details like package name and version. It then prompts the user (unless skipped) to confirm which platforms (iOS IPA, Android AAB) to upload.

It updates Fastlane configuration files and executes Fastlane upload commands for the selected platforms.

clientId The ID of the client whose apps are to be uploaded. skipAll If true, all user prompts will be skipped. skipAndroidUploadCheck If true, the prompt for Android upload will be skipped. skipIOSUploadCheck If true, the prompt for iOS upload will be skipped.

Throws a CustomException if configuration files are not found, package name or version are empty, or if required build files (IPA/AAB) do not exist for the selected upload platforms.

Implementation

Future<void> uploadApps(
  String clientId, {

  bool skipAll = false,
  bool skipAndroidUploadCheck = false,
  bool skipIOSUploadCheck = false,
}) async {
  final configFilePath = './clonify/clones/$clientId/config.json';
  const pubspecFilePath = './pubspec.yaml';

  // Check if config.json exists
  final configFile = File(configFilePath);
  if (!configFile.existsSync()) {
    logger.e('❌ Config file not found for client ID: $clientId');
    return;
  }

  // Parse config.json to get the packageName
  String packageName;
  try {
    final configContent = jsonDecode(configFile.readAsStringSync());
    packageName = configContent['packageName'] ?? 'Unknown Package Name';
  } catch (e) {
    logger.e('❌ Failed to read or parse $configFilePath: $e');
    return;
  }

  // Read pubspec.yaml to get the version
  String version;
  try {
    final pubspecContent = File(pubspecFilePath).readAsStringSync();
    final pubspecMap = loadYaml(pubspecContent);
    version = pubspecMap['version'] ?? 'Unknown Version';
  } catch (e) {
    logger.e('❌ Failed to read or parse $pubspecFilePath: $e');
    return;
  }

  final uploadIOSAnswer = (skipAll || skipAndroidUploadCheck) == true
      ? true
      : prompt('Do you want to upload the iOS IPA? (y/n):');

  final uploadIOS = uploadIOSAnswer == true
      ? true
      : (uploadIOSAnswer as String).toLowerCase() == 'y';

  final uploadAndroidAnswer = (skipAll || skipAndroidUploadCheck) == true
      ? true
      : prompt('Do you want to upload the Android AAB? (y/n):');
  final uploadAndroid = uploadAndroidAnswer == true
      ? true
      : (uploadAndroidAnswer as String).toLowerCase() == 'y';

  if (packageName.isEmpty || version.isEmpty) {
    throw CustomException(
      'Package name and version cannot be empty. During upload.',
    );
  }
  final ipaFile = File(Constants.ipaPath(packageName));
  if (!ipaFile.existsSync() && uploadIOS) {
    throw CustomException('iOS IPA path does not exist. During upload.');
  }
  final aabFile = File(Constants.aabPath);

  if (!aabFile.existsSync() && uploadAndroid) {
    throw CustomException('Android AAB path does not exist. During upload.');
  }

  Future.wait([
    if (uploadAndroid)
      updateFastlaneFiles(
        fastlanePath: 'android/fastlane/Fastfile',
        bundleId: packageName,
        appVersion: version.split('+').first,
        appVersionCode: version.split('+').last,
      ),
    if (uploadAndroid)
      runCommand(
        'fastlane',
        ['upload'],
        workingDirectory: 'android',
        successMessage: '✅ Uploaded Android build!',
      ),

    if (uploadIOS)
      updateFastlaneFiles(
        fastlanePath: 'ios/fastlane/Fastfile',
        bundleId: packageName,
        appVersion: version,
      ),
    if (uploadIOS)
      runCommand(
        'fastlane',
        ['upload'],
        workingDirectory: 'ios',
        successMessage: '✅ Uploaded iOS build!',
      ),
  ]);
}