oto/apps/runner/lib/cli/commands/command_validate.dart
toki 86afabb3eb refactor(runner): 런타임 패키지를 앱 하위로 이동한다
독립 control plane 구성을 위해 기존 Dart CLI/runtime을 runner 앱 경계로 옮기고, 후속 client/core/root 작업을 같은 마일스톤 task group에 연결한다.
2026-06-05 06:34:45 +09:00

110 lines
3 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:oto/cli/commands/command_base.dart';
import 'package:oto/oto/application.dart';
class CommandValidateCli extends CommandBase {
final Future<void> Function(String) _printString;
CommandValidateCli({Future<void> Function(String)? printString})
: _printString = printString ??
((value) async {
stdout.writeln(value);
});
@override
String get name => 'validate';
@override
String get usage => '[arguments]';
@override
String getDescription() =>
'Validate the pipeline yaml file structure and semantics without executing it.';
@override
Map<String, List<String>> get arguments => {
'-f {file path}': ['Validate the yaml file without executing it.'],
'--json': ['Output validation result as structured JSON format.'],
};
@override
bool shouldPrintExecuteLog(List<String> parameters) {
return !parameters.contains('--json');
}
@override
bool shouldExecuteWithoutParameters() => false;
@override
Future execute(List<String> parameters) async {
String? filePath;
bool isJson = false;
for (int i = 0; i < parameters.length; i++) {
final param = parameters[i];
if (param == '--json') {
isJson = true;
} else if (param.startsWith('-f')) {
if (param == '-f') {
if (i + 1 < parameters.length) {
filePath = parameters[++i];
} else {
throw Exception('Missing value for -f');
}
} else {
filePath = param.substring(2);
}
} else if (param.startsWith('--file=')) {
filePath = param.substring('--file='.length);
} else if (param == '--file') {
if (i + 1 < parameters.length) {
filePath = parameters[++i];
} else {
throw Exception('Missing value for --file');
}
} else {
throw Exception('Unknown option: $param');
}
}
if (filePath == null) {
throw Exception('Missing required option: -f {file path}');
}
final file = File(filePath);
YamlValidationResult result;
if (!file.existsSync()) {
result = YamlValidationResult.failure(
phase: 'Read YAML file',
message: 'There are no files in path "$filePath"',
exitCode: 10,
);
} else {
try {
final content = file.readAsStringSync();
result = Application.validateYamlContent(content);
} catch (e) {
result = YamlValidationResult.failure(
phase: 'Read YAML file',
message: e.toString(),
exitCode: 10,
);
}
}
exitCode = result.exitCode;
if (isJson) {
final jsonMap = result.toJson();
await _printString(const JsonEncoder.withIndent(' ').convert(jsonMap));
} else {
if (result.valid) {
await _printString('Valid YAML.');
} else {
await _printString('Invalid YAML: [${result.phase}] ${result.message}');
}
}
}
}