executeLane method

Future<DeploymentResult> executeLane({
  1. required String projectPath,
  2. required BuildPlatform platform,
  3. required String lane,
  4. String? artifactPath,
  5. bool dryRun = false,
  6. Map<String, String> environment = const {},
})

Execute a fastlane deployment lane.

Implementation

Future<DeploymentResult> executeLane({
  required String projectPath,
  required BuildPlatform platform,
  required String lane,
  String? artifactPath,
  bool dryRun = false,
  Map<String, String> environment = const {},
}) async {
  final steps = <String>[];
  final platformStr = platform.name.toLowerCase();
  final args = <String>[platformStr, lane];

  if (artifactPath != null) {
    args.addAll(['artifact:$artifactPath']);
  }

  final sanitizedEnv = sanitizeEnvironmentForLog(environment);
  final envSummary =
      sanitizedEnv.entries.map((e) => '${e.key}=${e.value}').join(', ');

  final cmdStr = 'fastlane ${args.join(' ')}';
  steps.add('Prepared Fastlane command: $cmdStr');
  if (envSummary.isNotEmpty) {
    steps.add('Environment variables configured: $envSummary');
  }

  if (dryRun) {
    steps.add('[DRY-RUN] Fastlane execution skipped.');
    return DeploymentResult.success(
      platform: platform,
      lane: lane,
      artifactPath: artifactPath,
      message:
          '[DRY-RUN] Fastlane deployment preview completed successfully.',
      executedSteps: steps,
      commandString: cmdStr,
      metadata: {
        'dryRun': true,
        'platform': platformStr,
        'lane': lane,
        'args': args,
      },
    );
  }

  // Determine directory: check if android/ios subfolder exists or run in project root
  String workingDir = projectPath;
  final platformSubdir = Directory('$projectPath/$platformStr');
  if (await platformSubdir.exists()) {
    workingDir = platformSubdir.path;
  }

  try {
    final processResult = await _processRunner.run(
      'fastlane',
      args,
      workingDirectory: workingDir,
      environment: environment,
    );

    if (processResult.stdout.toString().isNotEmpty) {
      steps.add('Fastlane stdout:\n${processResult.stdout}');
    }

    if (processResult.exitCode == 0) {
      steps.add('Fastlane lane "$lane" completed successfully.');
      return DeploymentResult.success(
        platform: platform,
        lane: lane,
        artifactPath: artifactPath,
        message: 'Fastlane deployment to $platformStr ($lane) succeeded.',
        executedSteps: steps,
        commandString: cmdStr,
        metadata: {'exitCode': 0},
      );
    } else {
      final stderrStr = processResult.stderr.toString();
      steps.add('Fastlane failed with exit code ${processResult.exitCode}.');
      return DeploymentResult.failure(
        platform: platform,
        lane: lane,
        artifactPath: artifactPath,
        message: 'Fastlane deployment to $platformStr ($lane) failed.',
        executedSteps: steps,
        errors: [
          if (stderrStr.isNotEmpty)
            stderrStr
          else
            'Fastlane exited with code ${processResult.exitCode}',
        ],
        commandString: cmdStr,
        metadata: {'exitCode': processResult.exitCode},
      );
    }
  } catch (e) {
    steps.add('Error executing fastlane: $e');
    return DeploymentResult.failure(
      platform: platform,
      lane: lane,
      artifactPath: artifactPath,
      message: 'Failed to run fastlane binary: $e',
      executedSteps: steps,
      errors: [e.toString()],
      commandString: cmdStr,
    );
  }
}