run method

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

Runs this command.

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

Implementation

@override
Future<int> run() async {
  final scanner = ProjectScanner();
  final context = scanner.scan();

  if (context == null) {
    print('✗ Current directory is not a Flutter project.');
    return 1;
  }

  final registry = const AuditRegistry();
  final engine = AuditEngine(audits: registry.getAudits());

  final spinner = Spinner('Running audits...')..start();
  final rawResults = await engine.run(context);
  spinner.stop();

  final ignoreConfig = await IgnoreConfigHelper.load(context);
  final (afterIgnore, suppressedCount) = ignoreConfig.apply(
    rawResults,
    context,
  );

  if (suppressedCount > 0) {
    print(
      'ⓘ $suppressedCount finding(s) suppressed by .flutter_auditor_ignore.yaml',
    );
    print('');
  }

  if (argResults!.flag('update-baseline')) {
    await BaselineHelper.write(afterIgnore, context);
    final total = afterIgnore.fold<int>(
      0,
      (sum, run) => sum + run.result.issues.length,
    );
    print(
      '✅ Baseline written to ${context.baselineFile.path} '
      '($total finding(s) recorded).',
    );
    print('   Future runs will only fail on newly introduced findings.');
    return 0;
  }

  final baselineConfig = await BaselineHelper.load(context);
  final (results, baselinedCount, exemptFromFailOn) = baselineConfig.apply(
    afterIgnore,
    context,
  );

  if (baselinedCount > 0) {
    final visibleCount = exemptFromFailOn.length;
    print(
      'ⓘ $baselinedCount pre-existing finding(s) accepted via '
      '.flutter_auditor_baseline.json'
      '${visibleCount > 0 ? ' ($visibleCount critical/high still shown, non-blocking)' : ''}',
    );
    print('');
  }

  final reporter = const ConsoleReporter();

  final verbose = argResults!.flag('verbose');
  final failOnArg = argResults!.option('fail-on')!;
  final failOn = failOnArg == 'none'
      ? null
      : Severity.values.firstWhere((severity) => severity.name == failOnArg);

  final exitCode = reporter.printReport(
    results,
    context: context,
    verbose: verbose,
    failOn: failOn,
    exemptFromFailOn: exemptFromFailOn,
  );

  final htmlPath = argResults!.option('html');
  if (htmlPath != null) {
    final htmlReporter = const HtmlReporter();
    final file = htmlReporter.writeReport(
      results,
      context: context,
      outputPath: htmlPath,
    );
    print('');
    print('📊 HTML report written to: ${file.path}');

    if (argResults!.flag('open')) {
      await _openInBrowser(file.path);
    }
  }

  final jsonPath = argResults!.option('json');
  if (jsonPath != null) {
    final jsonReporter = const JsonReporter();
    final file = jsonReporter.writeReport(
      results,
      context: context,
      outputPath: jsonPath,
    );
    print('');
    print('📄 JSON report written to: ${file.path}');
  }

  final sarifPath = argResults!.option('sarif');
  if (sarifPath != null) {
    final sarifReporter = const SarifReporter();
    final file = sarifReporter.writeReport(
      results,
      context: context,
      outputPath: sarifPath,
    );
    print('');
    print('📄 SARIF report written to: ${file.path}');
  }

  return exitCode;
}