recoverRelease method

Future<bool> recoverRelease(
  1. String projectPath, {
  2. String? stage,
  3. bool dryRun = false,
})

Performs deployment-only recovery for a previously failed deployment stage.

Implementation

Future<bool> recoverRelease(
  String projectPath, {
  String? stage,
  bool dryRun = false,
}) async {
  final currentState = await _stateTracker.loadCurrentState(projectPath);

  if (currentState == null) {
    _output.error(
        'Recovery failed: No recorded failed release state found in "$projectPath".');
    return false;
  }

  final targetStage = stage ?? currentState.failedStage ?? 'deployment';

  if (targetStage != 'deployment') {
    _output.error(
        'Recovery error: Automated recovery is only supported for "deployment" stage failures to prevent destructive operations.');
    _output.info(
        'Current failed stage is "$targetStage". Re-run release explicitly after resolving issues.');
    return false;
  }

  _output.info(
      'Initiating deployment recovery for version ${currentState.versionName}+${currentState.buildNumber}...');
  if (currentState.artifactPath != null) {
    final valid = await _artifactIntegrity.verifyArtifact(
      artifactPath: currentState.artifactPath!,
      expectedChecksum: currentState.artifactChecksum,
    );
    if (!valid) {
      _output.error(
          'Recovery failed: Artifact at "${currentState.artifactPath}" missing or checksum mismatch.');
      return false;
    }
    _output.success('Artifact verified: ${currentState.artifactPath}');
  }

  if (dryRun) {
    _output.info(
        '[DRY-RUN] Previewed deployment recovery. Fastlane execution skipped.');
    return true;
  }

  final platform = BuildPlatform.parse(currentState.platform ?? 'android');
  final deployContext = DeploymentContext(
    projectPath: projectPath,
    platform: platform,
    artifactPath: currentState.artifactPath,
    lane:
        currentState.environment == 'production' ? 'production' : 'internal',
    versionName: currentState.versionName,
    buildNumber: currentState.buildNumber,
    dryRun: false,
  );

  final results = await _deploymentManager.deploy(deployContext);
  final allSuccess = results.every((r) => r.success);

  if (allSuccess) {
    await _stateTracker.saveState(
      projectPath,
      currentState.copyWith(
        status: ReleaseStatus.completed,
        failedStage: null,
      ),
    );
    _output.success('Deployment recovery completed successfully.');
    return true;
  } else {
    _output.error('Deployment recovery failed during Fastlane execution.');
    return false;
  }
}