From 9ed6b8b9b658312b520b91e6290d5907aa16a794 Mon Sep 17 00:00:00 2001 From: Pebkac03 Date: Mon, 10 Feb 2025 03:42:02 +0100 Subject: [PATCH 1/5] chore: added package async as dependecy --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index a93c1e4..30265bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: pub_updater: ^0.4.0 http: ^1.1.2 hive: ^2.2.3 + async: ^2.13.0 dev_dependencies: lints: ^3.0.0 From a810d966125f239d59be3b153eee5a4a42cc5c0f Mon Sep 17 00:00:00 2001 From: Pebkac03 Date: Mon, 10 Feb 2025 03:50:46 +0100 Subject: [PATCH 2/5] feat: made ProcessService log both stdout and stderr --- lib/src/services/process_service.dart | 43 ++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/src/services/process_service.dart b/lib/src/services/process_service.dart index a400ed3..1aa2a9c 100644 --- a/lib/src/services/process_service.dart +++ b/lib/src/services/process_service.dart @@ -1,6 +1,8 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:async/async.dart'; import 'package:stacked_cli/src/constants/command_constants.dart'; import 'package:stacked_cli/src/locator.dart'; import 'package:stacked_cli/src/services/colorized_log_service.dart'; @@ -166,17 +168,43 @@ class ProcessService { final lines = []; final lineSplitter = LineSplitter(); - await process.stdout.transform(utf8.decoder).forEach((output) { - if (verbose) _cLog.flutterOutput(message: output); + + final Stream> infoStream = + process.stdout.transform(utf8.decoder).transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add( + IdStreamResponse('info', data), + ), + ), + ); + + final Stream> errorStream = + process.stderr.transform(utf8.decoder).transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add( + IdStreamResponse('error', data), + ), + ), + ); + + final Stream> groupedStream = + StreamGroup.merge([infoStream, errorStream]); + + await for (final value in groupedStream) { + if (value.id == 'error') { + _cLog.error(message: value.value); + } else if (value.id == 'info' && verbose) { + _cLog.flutterOutput(message: value.value); + } if (handleOutput != null) { lines.addAll(lineSplitter - .convert(output) + .convert(value.value) .map((l) => l.trim()) .where((l) => l.isNotEmpty) .toList()); } - }); + } await handleOutput?.call(lines); @@ -221,3 +249,10 @@ class ProcessService { ); } } + +class IdStreamResponse { + final String id; + final T value; + + IdStreamResponse(this.id, this.value); +} From 6ea55ccb1008bfaa8762afeff9be08ea6180b8f3 Mon Sep 17 00:00:00 2001 From: Pebkac03 Date: Mon, 10 Feb 2025 04:06:27 +0100 Subject: [PATCH 3/5] feat: made _runProcess throw on failed subcommand --- lib/src/services/process_service.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/src/services/process_service.dart b/lib/src/services/process_service.dart index 1aa2a9c..dad1984 100644 --- a/lib/src/services/process_service.dart +++ b/lib/src/services/process_service.dart @@ -192,7 +192,7 @@ class ProcessService { await for (final value in groupedStream) { if (value.id == 'error') { - _cLog.error(message: value.value); + throw SubCommandException(value.value); } else if (value.id == 'info' && verbose) { _cLog.flutterOutput(message: value.value); } @@ -220,6 +220,8 @@ class ProcessService { message: message, stackTrace: s.toString(), ); + } on SubCommandException catch (e, _) { + rethrow; } catch (e, s) { final message = 'Command failed. Command executed: $programName ${arguments.join(' ')}\nException: ${e.toString()}'; @@ -256,3 +258,11 @@ class IdStreamResponse { IdStreamResponse(this.id, this.value); } + +class SubCommandException implements Exception { + final dynamic msg; + SubCommandException(this.msg); + + @override + toString() => msg.toString(); +} From 0202b9820d42884bfec3ea23b3f8ee8d41025ba5 Mon Sep 17 00:00:00 2001 From: Pebkac03 Date: Mon, 10 Feb 2025 04:10:08 +0100 Subject: [PATCH 4/5] doc: changed doc comment of _runProcess to reflect changed functionality --- lib/src/services/process_service.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/services/process_service.dart b/lib/src/services/process_service.dart index dad1984..6baecb6 100644 --- a/lib/src/services/process_service.dart +++ b/lib/src/services/process_service.dart @@ -137,6 +137,7 @@ class ProcessService { } /// It runs a process and logs the output to the console when [verbose] is true. + /// Will throw if the process errors out and won't run any subsequent processes. /// /// Args: /// programName (String): The name of the program to run. From dfce83d7a8ac0830eee1cd127f3af5fb41f41d43 Mon Sep 17 00:00:00 2001 From: Pebkac03 Date: Mon, 10 Feb 2025 04:34:35 +0100 Subject: [PATCH 5/5] refactor: moved custom exception to it's own file, renamed thing, added doc comments. --- .../stacked_process_failed_exception.dart | 7 +++++ lib/src/services/process_service.dart | 30 ++++++++----------- 2 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 lib/src/exceptions/stacked_process_failed_exception.dart diff --git a/lib/src/exceptions/stacked_process_failed_exception.dart b/lib/src/exceptions/stacked_process_failed_exception.dart new file mode 100644 index 0000000..3f73205 --- /dev/null +++ b/lib/src/exceptions/stacked_process_failed_exception.dart @@ -0,0 +1,7 @@ +class StackedProcessFailedException implements Exception { + final dynamic msg; + StackedProcessFailedException(this.msg); + + @override + toString() => msg.toString(); +} diff --git a/lib/src/services/process_service.dart b/lib/src/services/process_service.dart index 6baecb6..edadb51 100644 --- a/lib/src/services/process_service.dart +++ b/lib/src/services/process_service.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:async/async.dart'; import 'package:stacked_cli/src/constants/command_constants.dart'; +import 'package:stacked_cli/src/exceptions/stacked_process_failed_exception.dart'; import 'package:stacked_cli/src/locator.dart'; import 'package:stacked_cli/src/services/colorized_log_service.dart'; import 'package:stacked_cli/src/services/config_service.dart'; @@ -170,30 +171,30 @@ class ProcessService { final lines = []; final lineSplitter = LineSplitter(); - final Stream> infoStream = + final Stream<_IdStreamResponse> infoStream = process.stdout.transform(utf8.decoder).transform( StreamTransformer.fromHandlers( handleData: (data, sink) => sink.add( - IdStreamResponse('info', data), + _IdStreamResponse('info', data), ), ), ); - final Stream> errorStream = + final Stream<_IdStreamResponse> errorStream = process.stderr.transform(utf8.decoder).transform( StreamTransformer.fromHandlers( handleData: (data, sink) => sink.add( - IdStreamResponse('error', data), + _IdStreamResponse('error', data), ), ), ); - final Stream> groupedStream = + final Stream<_IdStreamResponse> groupedStream = StreamGroup.merge([infoStream, errorStream]); await for (final value in groupedStream) { if (value.id == 'error') { - throw SubCommandException(value.value); + throw StackedProcessFailedException(value.value); } else if (value.id == 'info' && verbose) { _cLog.flutterOutput(message: value.value); } @@ -221,7 +222,7 @@ class ProcessService { message: message, stackTrace: s.toString(), ); - } on SubCommandException catch (e, _) { + } on StackedProcessFailedException catch (e, _) { rethrow; } catch (e, s) { final message = @@ -253,17 +254,12 @@ class ProcessService { } } -class IdStreamResponse { +/// A simple wrapper class for values emitted by stderr- and stdout streams +/// to differentiate between them when merged. +/// [id] should be either 'error' or 'info'. +class _IdStreamResponse { final String id; final T value; - IdStreamResponse(this.id, this.value); -} - -class SubCommandException implements Exception { - final dynamic msg; - SubCommandException(this.msg); - - @override - toString() => msg.toString(); + _IdStreamResponse(this.id, this.value); }