execute method
Public method or function.
Implementation
@override
/// Public method or function.
Future<int> execute(List<String> args) async {
var asJson = false;
Severity failOn = Severity.error;
double? minScore;
String? outputPath;
String? projectPath;
for (var i = 0; i < args.length; i++) {
final arg = args[i];
if (arg == '--json') {
asJson = true;
} else if (arg == '--output') {
if (i + 1 < args.length) {
outputPath = args[++i];
} else {
terminal.writeError('Missing value for --output flag');
return AppConstants.exitInternalFailure;
}
} else if (arg == '--fail-on') {
if (i + 1 < args.length) {
final parsed = _tryParseSeverity(args[++i]);
if (parsed == null) {
terminal.writeError(
'Invalid --fail-on value. Must be one of: warning, error, critical.',
);
return AppConstants.exitInternalFailure;
}
failOn = parsed;
} else {
terminal.writeError('Missing value for --fail-on flag');
return AppConstants.exitInternalFailure;
}
} else if (arg == '--min-score') {
if (i + 1 < args.length) {
final parsed = double.tryParse(args[++i]);
if (parsed == null || parsed < 0 || parsed > 100) {
terminal.writeError(
'Invalid --min-score value. Must be a number between 0 and 100.',
);
return AppConstants.exitInternalFailure;
}
minScore = parsed;
} else {
terminal.writeError('Missing value for --min-score flag');
return AppConstants.exitInternalFailure;
}
} else {
projectPath = arg;
}
}
projectPath ??= fileSystem.currentDirectory;
if (!fileSystem.exists(projectPath) ||
!fileSystem.isDirectory(projectPath)) {
terminal.writeError('Project path does not exist: $projectPath');
return AppConstants.exitInternalFailure;
}
final context = AnalyzerContext(
projectPath: projectPath,
fileSystem: fileSystem,
);
// When --json is used without --output, route progress messages to stderr
// so the JSON payload on stdout stays clean for piping.
final Terminal progressTerminal = asJson && outputPath == null
? const _StderrTerminal()
: terminal;
final Logger progressLogger = asJson && outputPath == null
? Logger(terminal: progressTerminal)
: logger;
List<DiagnosticResult> results;
try {
results = await analyzerService.runAll(
context,
progressLogger: progressLogger,
);
} catch (e) {
terminal.writeError('Report generation failed: $e');
return AppConstants.exitInternalFailure;
}
final reportService = ReportService(terminal: progressTerminal);
final projectName = results
.map((r) => r.projectName)
.firstWhere((n) => n != null, orElse: () => null);
var report = reportService.generateReport(
results: results,
projectName: projectName,
projectPath: projectPath,
);
// Re-compute health score if minScore is set to ensure it's always present
if (minScore != null && report.healthScore == null) {
report = report.computeHealthScore();
}
if (asJson && outputPath != null) {
await reportService.saveReport(report, fileSystem, outputPath);
progressTerminal.writeSuccess('Report saved to $outputPath');
progressTerminal.writeLine('');
} else if (asJson) {
// JSON payload goes through the original terminal (stdout in prod,
// fake terminal in tests) so it can be piped.
terminal.writeLine(reportService.toJson(report));
} else if (outputPath != null) {
progressTerminal.writeLine('Generating report for $projectPath...');
progressTerminal.writeLine('');
await reportService.saveReport(report, fileSystem, outputPath);
progressTerminal.writeSuccess('Report saved to $outputPath');
progressTerminal.writeLine('');
reportService.printReport(report);
} else {
progressTerminal.writeLine('Generating report for $projectPath...');
progressTerminal.writeLine('');
reportService.printReport(report);
}
// Check --min-score threshold
if (minScore != null) {
final score = report.healthScore?.overallScore ?? report.score;
if (score < minScore) {
terminal.writeWarning(
'Score ${score.toStringAsFixed(1)} is below threshold $minScore',
);
return AppConstants.exitInternalFailure;
}
}
// Check --fail-on threshold
final mostSevere = report.mostSevereRank;
final failOnRank = failOn.value + 1;
if (mostSevere >= failOnRank) {
return report.exitCode;
}
return AppConstants.exitNoIssues;
}