execute method

  1. @override
Future<int> execute(
  1. List<String> arguments
)
override

Executes the command with the provided arguments. Returns an integer exit code (0 for success).

Implementation

@override
Future<int> execute(List<String> arguments) async {
  final ArgResults results;
  try {
    results = _argParser.parse(arguments);
  } on FormatException catch (e) {
    throw ValidationException(
      'Error: ${e.message}\n\nUsage:\n  flutter_setup release [--mode normal|shorebird] [--patch|--minor|--major|--version X] [--dry-run]',
    );
  }

  final targetPath = results['path'] as String? ?? '.';

  // Handle sub-actions (info, history, verify, summary, notes)
  if (results.rest.isNotEmpty) {
    final action = results.rest.first;
    if (action == 'info') {
      return _handleInfo(targetPath);
    } else if (action == 'history') {
      return _handleHistory(targetPath);
    } else if (action == 'status') {
      final statusText = await _recoveryManager.getReleaseStatus(targetPath);
      _output.info(statusText);
      return 0;
    } else if (action == 'verify') {
      final verifyText = await _recoveryManager.verifyRelease(targetPath);
      _output.info(verifyText);
      return 0;
    } else if (action == 'recover') {
      final success = await _recoveryManager.recoverRelease(
        targetPath,
        dryRun: results['dry-run'] as bool? ?? false,
      );
      return success ? 0 : 1;
    } else if (action == 'summary') {
      final summaryCmd = ReleaseSummaryCommand(_output);
      return summaryCmd.execute(arguments.sublist(1));
    } else if (action == 'notes') {
      final notesCmd = ReleaseNotesCommand(_output);
      return notesCmd.execute(arguments.sublist(1));
    }
  }

  final modeRaw = results['mode'] as String? ?? 'normal';
  final dryRun = results['dry-run'] as bool? ?? false;
  final allowDirty = results['allow-dirty'] as bool? ?? false;

  final patchFlag = results['patch'] as bool? ?? false;
  final minorFlag = results['minor'] as bool? ?? false;
  final majorFlag = results['major'] as bool? ?? false;
  final explicitVersion = results['version'] as String?;

  final explicitBuildStr = results['build-number'] as String?;
  int? explicitBuildNumber;
  if (explicitBuildStr != null && explicitBuildStr.isNotEmpty) {
    explicitBuildNumber = int.tryParse(explicitBuildStr);
    if (explicitBuildNumber == null || explicitBuildNumber <= 0) {
      throw const ValidationException(
          'Build number must be a positive integer.');
    }
  }

  // Determine VersionBumpMode
  VersionBumpMode bumpMode = VersionBumpMode.none;
  final int bumpFlagsCount = (patchFlag ? 1 : 0) +
      (minorFlag ? 1 : 0) +
      (majorFlag ? 1 : 0) +
      (explicitVersion != null ? 1 : 0);
  if (bumpFlagsCount > 1) {
    throw const ValidationException(
      'Cannot specify multiple version bump options (--patch, --minor, --major, --version) simultaneously.',
    );
  }

  if (patchFlag) {
    bumpMode = VersionBumpMode.patch;
  } else if (minorFlag) {
    bumpMode = VersionBumpMode.minor;
  } else if (majorFlag) {
    bumpMode = VersionBumpMode.major;
  } else if (explicitVersion != null && explicitVersion.isNotEmpty) {
    bumpMode = VersionBumpMode.explicit;
  } else if (explicitBuildNumber != null) {
    bumpMode = VersionBumpMode.buildNumberOnly;
  }

  // Validate that projectPath points to a valid Flutter project
  Validators.validateExistingFlutterProject(Directory(targetPath));

  final releaseType = ReleaseType.fromString(modeRaw);
  if (releaseType == ReleaseType.shorebirdPatch) {
    throw const ValidationException(
      'Use "flutter_setup patch" to apply Shorebird OTA patches.',
    );
  }

  final platformRaw = results['platform'] as String? ?? 'android';
  final formatRaw = results['format'] as String?;
  final deployFlag = results['deploy'] as bool? ?? false;
  final laneRaw = results['lane'] as String?;
  final ciFlag = results['ci'] as bool? ?? false;
  final tagFlag = results['tag'] as bool? ?? false;
  final tagPrefix = results['tag-prefix'] as String? ?? 'v';

  final envArg =
      results['environment'] as String? ?? results['env'] as String?;
  final rawDefines =
      (results['dart-define'] as List<String>?) ?? const <String>[];
  final parsedDefines = EnvironmentResolver.parseDartDefines(rawDefines);

  final resolver = const EnvironmentResolver();
  final envConfig = resolver.resolve(
    cliEnvironment: envArg,
    cliFlavor: results['flavor'] as String?,
    cliDartDefines: parsedDefines,
    envVars: Platform.environment,
  );

  final context = ReleaseContext(
    projectPath: targetPath,
    type: releaseType,
    dryRun: dryRun,
    allowDirty: allowDirty,
    versionBumpMode: bumpMode,
    targetPlatform: platformRaw,
    versionName: explicitVersion,
    buildNumber: explicitBuildNumber,
    config: ReleaseConfig(
      targetPlatform: platformRaw,
      format: formatRaw,
      deploy: deployFlag,
      lane: laneRaw ?? envConfig.lane,
      isCi: ciFlag,
      createTag: tagFlag,
      tagPrefix: tagPrefix,
      environmentName: envArg != null ? envConfig.name : null,
      flavor: envConfig.flavor,
      dartDefines: envConfig.dartDefines,
    ),
  );

  final result = await _manager.executeRelease(context);

  if (!result.success) {
    for (final err in result.errors) {
      _output.error('Error: $err');
    }
    return 1;
  }

  return 0;
}