// ignore_for_file: avoid_init_to_null import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:dart_framework/data/isolate_data.dart'; import 'package:dart_framework/platform/isolate_manager.dart'; import 'package:oto/cli/cli.dart'; import 'package:oto/cli/commands/command_base.dart'; import 'package:dart_framework/utils/system_util.dart'; import 'package:oto/oto/application.dart'; import 'package:oto/oto/core/build_result.dart'; import 'package:oto/oto/data/command_data.dart'; class CommandExe extends CommandBase { IsolateHandler? _handler; bool _complete = false; bool _ready = false; int _exitCode = 0; Map? _resultJson; final Future Function(String) _printString; CommandExe({Future Function(String)? printString}) : _printString = printString ?? ((value) async { stdout.writeln(value); }); @override Map> get arguments => { '-j': ["Used when it's a Jenkins build."], '-t': ["This is the setup for testing."], '-f {file path}': ["Run the yaml file to process."], '--file {file path}': ["Run the yaml file to process."], '--json': ["Output execution result as structured JSON format."] }; @override String get name => 'exe'; @override bool shouldPrintExecuteLog(List parameters) { return !parameters.contains('--json'); } @override Future execute(List parameters) async { BuildType? buildType; String? filePath; bool isJson = false; bool hasUnknownOption = false; if (parameters.isEmpty) { printHelp(); return simpleFuture; } for (int i = 0; i < parameters.length; i++) { final param = parameters[i]; if (param == '--json') { isJson = true; } else if (param == '-j') { buildType = BuildType.jenkins; } else if (param == '-t') { buildType = BuildType.test; } else if (param.startsWith('-f')) { buildType = BuildType.file; if (param == '-f') { if (i + 1 < parameters.length) { filePath = parameters[++i]; } else { hasUnknownOption = true; } } else { filePath = param.substring(2); } } else if (param.startsWith('--file=')) { buildType = BuildType.file; filePath = param.substring('--file='.length); } else if (param == '--file') { buildType = BuildType.file; if (i + 1 < parameters.length) { filePath = parameters[++i]; } else { hasUnknownOption = true; } } else { hasUnknownOption = true; } } if (hasUnknownOption || buildType == null) { printHelp(); return simpleFuture; } final logEnable = !isJson; if (buildType == BuildType.file) { if (filePath == null || filePath.isEmpty) { printHelp(); return simpleFuture; } await startYaml(filePath, logEnable: logEnable); } else { _handler = IsolateManager.create(IsolateExe(buildType, logEnable: logEnable)); await isComplete(); } if (isJson && _resultJson != null) { await _printString(const JsonEncoder.withIndent(' ').convert(_resultJson)); } exitCode = _exitCode; _handler?.exit(); return simpleFuture; } Future executeScheduler( DataBuild build, bool logEnable, String logPath) async { _handler = IsolateManager.create(IsolateExe.scheduler(build, logEnable, logPath)); await isComplete(); _handler?.exit(); return simpleFuture; } Future isComplete() async { _ready = false; _complete = false; _exitCode = 0; _resultJson = null; _handler!.addEventListener(IsolateEvent.ready, (data) => _ready = true); _handler!.addListener('complete', (data) { if (data is Map) { _resultJson = Map.from(data); _exitCode = (_resultJson!['exitCode'] as num?)?.toInt() ?? 10; } else if (data is int) { _exitCode = data; } _complete = true; }); while (!(_complete && _ready)) { await Future.delayed(const Duration(milliseconds: 200)); } return simpleFuture; } Future startYaml(String target, {bool logEnable = true}) async { var file = File(target); if (file.existsSync()) { _handler = IsolateManager.create(IsolateExe(BuildType.file, logEnable: logEnable, yamlContent: file.readAsStringSync( /*encoding: Platform.isWindows ? eucKr : systemEncoding*/))); await isComplete(); } else { _exitCode = 10; if (!logEnable) { final failureResult = BuildResult.failure( 'There are no files in path "$target"', null, exitCode: 10, ); _resultJson = failureResult.toJson(); } else { await CLI.print([ CLI.style('There are no files in path "$target"', color: Color.red) ]); } } return simpleFuture; } @override String getDescription() { return ':::: Read documents written in yaml to do process task.'; } } class IsolateExe extends IsolateBase { late BuildType _buildType; bool _logEnable = true; DataBuild? _build = null; String? _yamleContent = null; String? _logPath = null; IsolateExe(this._buildType, {String? yamlContent, DataBuild? build, bool logEnable = true, String? logPath}) { _yamleContent = yamlContent; _build = build; _logEnable = logEnable; _logPath = logPath; } factory IsolateExe.scheduler( DataBuild build, bool logEnable, String logPath) { return IsolateExe(BuildType.scheduler, build: build, logEnable: logEnable, logPath: logPath); } @override void onReady(Object? initData) async { final result = await Application.instance.build(_buildType, yamlContent: _yamleContent, buildData: _build, logEnable: _logEnable, logPath: _logPath); send(IsoMessege('complete', data: result.toJson())); } }