diff --git a/bin/dependency_services.dart b/bin/dependency_services.dart index 3dabf5d..f117afd 100644 --- a/bin/dependency_services.dart +++ b/bin/dependency_services.dart
@@ -46,6 +46,7 @@ usageLineLength: lineLength) { argParser.addFlag('verbose', abbr: 'v', negatable: false, help: 'Shortcut for "--verbosity=all".'); + PubTopLevel.addColorFlag(argParser); argParser.addOption( 'directory', abbr: 'C',
diff --git a/lib/src/command.dart b/lib/src/command.dart index a1a8a2e..ae942ea 100644 --- a/lib/src/command.dart +++ b/lib/src/command.dart
@@ -178,7 +178,8 @@ @override @nonVirtual FutureOr<int> run() async { - computeCommand(_pubTopLevel.argResults); + _computeCommand(_pubTopLevel.argResults); + _decideOnColors(_pubTopLevel.argResults); log.verbosity = _pubTopLevel.verbosity; log.fine('Pub ${sdk.version}'); @@ -300,7 +301,17 @@ /// returned. For instance `install` becomes `get`. static final String command = _command ?? ''; - static void computeCommand(ArgResults argResults) { + static void _decideOnColors(ArgResults argResults) { + if (!argResults.wasParsed('color')) { + forceColors = ForceColorOption.auto; + } else { + forceColors = argResults['color'] as bool + ? ForceColorOption.always + : ForceColorOption.never; + } + } + + static void _computeCommand(ArgResults argResults) { var list = <String?>[]; for (var command = argResults.command; command != null; @@ -329,6 +340,17 @@ bool get captureStackChains; log.Verbosity get verbosity; bool get trace; + + static addColorFlag(ArgParser argParser) { + argParser.addFlag( + 'color', + help: 'Use colors in terminal output.\n' + 'Defaults to color when connected to a ' + 'terminal, and no-color otherwise.', + ); + } + + /// The directory containing the pubspec.yaml of the project to work on. String? get directory; /// The argResults from the level of parsing of the 'pub' command.
diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart index 601bdc8..59131c4 100644 --- a/lib/src/command/outdated.dart +++ b/lib/src/command/outdated.dart
@@ -47,13 +47,6 @@ OutdatedCommand() { argParser.addFlag( - 'color', - help: 'Whether to color the output.\n' - 'Defaults to color when connected to a ' - 'terminal, and no-color otherwise.', - ); - - argParser.addFlag( 'dependency-overrides', defaultsTo: true, help: 'Show resolutions with `dependency_overrides`.', @@ -270,14 +263,8 @@ includeDevDependencies: includeDevDependencies, ); } else { - if (argResults.wasParsed('color')) { - forceColors = argResults['color']; - } - final useColors = - argResults.wasParsed('color') ? argResults['color'] : canUseAnsiCodes; - await _outputHuman(rows, mode, - useColors: useColors, + useColors: canUseAnsiCodes, showAll: showAll, includeDevDependencies: includeDevDependencies, lockFileExists: fileExists(entrypoint.lockFilePath),
diff --git a/lib/src/command_runner.dart b/lib/src/command_runner.dart index 21f568b..92d4cf8 100644 --- a/lib/src/command_runner.dart +++ b/lib/src/command_runner.dart
@@ -121,6 +121,7 @@ }); argParser.addFlag('verbose', abbr: 'v', negatable: false, help: 'Shortcut for "--verbosity=all".'); + PubTopLevel.addColorFlag(argParser); argParser.addOption( 'directory', abbr: 'C', @@ -155,7 +156,8 @@ @override Future<int> run(Iterable<String> args) async { try { - _argResults = parse(args); + final argResults = parse(args); + _argResults = argResults; return await runCommand(argResults) ?? exit_codes.SUCCESS; } on UsageException catch (error) { log.exception(error);
diff --git a/lib/src/io.dart b/lib/src/io.dart index 6422f84..50f7eb9 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart
@@ -390,7 +390,8 @@ return null; } - for (var i = 0; i < 3; i++) { + const maxRetries = 50; + for (var i = 0; i < maxRetries; i++) { try { operation(); break; @@ -398,7 +399,7 @@ var reason = getErrorReason(error); if (reason == null) rethrow; - if (i < 2) { + if (i < maxRetries - 1) { log.io('Pub failed to $description because $reason. ' 'Retrying in 50ms.'); sleep(Duration(milliseconds: 50));
diff --git a/lib/src/pub_embeddable_command.dart b/lib/src/pub_embeddable_command.dart index b9e59c6..c7c7f87 100644 --- a/lib/src/pub_embeddable_command.dart +++ b/lib/src/pub_embeddable_command.dart
@@ -61,10 +61,12 @@ final bool Function() isVerbose; PubEmbeddableCommand(this.analytics, this.isVerbose) : super() { - argParser.addFlag('trace', - help: 'Print debugging information when an error occurs.'); + // This flag was never honored in the embedding but since it was accepted we + // leave it as a hidden flag to avoid breaking clients that pass it. + argParser.addFlag('trace', hide: true); argParser.addFlag('verbose', abbr: 'v', negatable: false, help: 'Print detailed logging.'); + PubTopLevel.addColorFlag(argParser); argParser.addOption( 'directory', abbr: 'C',
diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 3e1cf48..e4451db 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart
@@ -389,8 +389,14 @@ String _urlDecode(String encoded) => Uri.decodeComponent(encoded.replaceAll('+', ' ')); -/// Set to `true` if ANSI colors should be output regardless of terminalD -bool forceColors = false; +enum ForceColorOption { + always, + never, + auto, +} + +/// Change to decide if ANSI colors should be output regardless of terminalD. +ForceColorOption forceColors = ForceColorOption.auto; /// Whether ansi codes such as color escapes are safe to use. /// @@ -398,8 +404,18 @@ /// /// Tests should make sure to run the subprocess with or without an attached /// terminal to decide if colors will be provided. -bool get canUseAnsiCodes => - forceColors || (stdout.hasTerminal && stdout.supportsAnsiEscapes); +bool get canUseAnsiCodes { + switch (forceColors) { + case ForceColorOption.always: + return true; + case ForceColorOption.never: + return false; + case ForceColorOption.auto: + return (!Platform.environment.containsKey('NO_COLOR')) && + stdout.hasTerminal && + stdout.supportsAnsiEscapes; + } +} /// Gets an ANSI escape if those are supported by stdout (or nothing). String getAnsi(String ansiCode) => canUseAnsiCodes ? ansiCode : '';
diff --git a/test/embedding/embedding_test.dart b/test/embedding/embedding_test.dart index a9795ea..73370d7 100644 --- a/test/embedding/embedding_test.dart +++ b/test/embedding/embedding_test.dart
@@ -39,10 +39,15 @@ ); await process.shouldExit(exitCode); + final stdoutLines = await process.stdout.rest.toList(); + final stderrLines = await process.stderr.rest.toList(); + buffer.writeln([ '\$ $_commandRunner ${args.join(' ')}', - ...await process.stdout.rest.map(_filter).toList(), - ...await process.stderr.rest.map((e) => '[E] ${_filter(e)}').toList(), + if (stdoutLines.isNotEmpty) _filter(stdoutLines.join('\n')), + if (stderrLines.isNotEmpty) + _filter(stderrLines.join('\n')) + .replaceAll(RegExp('^', multiLine: true), '[E] '), ].join('\n')); buffer.write('\n'); } @@ -228,6 +233,31 @@ await runEmbeddingToBuffer(['--verbose', 'pub', 'logout'], buffer); expect(buffer.toString(), contains('FINE: Pub 0.1.2+3')); }); + + testWithGolden('--help', (context) async { + await servePackages(); + await context.runEmbedding( + ['pub', '--help'], + workingDirectory: d.path('.'), + ); + }); + + testWithGolden('--color forces colors', (context) async { + final server = await servePackages(); + server.serve('foo', '1.0.0'); + server.serve('foo', '2.0.0'); + await d.appDir({'foo': '^1.0.0'}).create(); + await context.runEmbedding( + ['pub', '--no-color', 'get'], + environment: getPubTestEnvironment(), + workingDirectory: d.path(appPath), + ); + await context.runEmbedding( + ['pub', '--color', 'get'], + workingDirectory: d.path(appPath), + environment: getPubTestEnvironment(), + ); + }); } String _filter(String input) { @@ -326,5 +356,14 @@ .replaceAll( RegExp(r'Writing \d+ characters', multiLine: true), r'Writing $N characters', - ); + ) + + /// TODO(sigurdm): This hack suppresses differences in stack-traces + /// between dart 2.17 and 2.18. Remove when 2.18 is stable. + .replaceAllMapped( + RegExp( + r'(^(.*)pub/src/command.dart \$LINE:\$COL(.*)$)\n\1', + multiLine: true, + ), + (match) => match[1]!); }
diff --git a/test/testdata/goldens/embedding/embedding_test/--color forces colors.txt b/test/testdata/goldens/embedding/embedding_test/--color forces colors.txt new file mode 100644 index 0000000..d468889 --- /dev/null +++ b/test/testdata/goldens/embedding/embedding_test/--color forces colors.txt
@@ -0,0 +1,15 @@ +# GENERATED BY: test/embedding/embedding_test.dart + +$ tool/test-bin/pub_command_runner.dart pub --no-color get +Resolving dependencies... ++ foo 1.0.0 (2.0.0 available) +Downloading foo 1.0.0... +Changed 1 dependency! + +-------------------------------- END OF OUTPUT --------------------------------- + +$ tool/test-bin/pub_command_runner.dart pub --color get +Resolving dependencies... + [1mfoo[0m 1.0.0 [36m(2.0.0 available)[39m +Got dependencies! +
diff --git a/test/testdata/goldens/embedding/embedding_test/--help.txt b/test/testdata/goldens/embedding/embedding_test/--help.txt new file mode 100644 index 0000000..ea8fd3f --- /dev/null +++ b/test/testdata/goldens/embedding/embedding_test/--help.txt
@@ -0,0 +1,33 @@ +# GENERATED BY: test/embedding/embedding_test.dart + +$ tool/test-bin/pub_command_runner.dart pub --help +Work with packages. + +Usage: pub_command_runner pub [arguments...] +-h, --help Print this usage information. +-v, --verbose Print detailed logging. + --[no-]color Use colors in terminal output. + Defaults to color when connected to a terminal, and + no-color otherwise. +-C, --directory=<dir> Run the subcommand in the directory<dir>. + (defaults to ".") + +Available subcommands: + add Add dependencies to pubspec.yaml. + cache Work with the system cache. + deps Print package dependencies. + downgrade Downgrade the current package's dependencies to oldest versions. + fail Throws an exception + get Get the current package's dependencies. + global Work with global packages. + login Log into pub.dev. + logout Log out of pub.dev. + outdated Analyze your dependencies to find which ones can be upgraded. + publish Publish the current package to pub.dartlang.org. + remove Removes a dependency from the current package. + token Manage authentication tokens for hosted pub repositories. + upgrade Upgrade the current package's dependencies to latest versions. + +Run "pub_command_runner help" to see global options. +See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation. +
diff --git a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt index fc69b03..bdc0165 100644 --- a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt +++ b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
@@ -249,7 +249,6 @@ [E] Bad state: Pub has crashed [E] tool/test-bin/pub_command_runner.dart $LINE:$COL ThrowingCommand.runProtected [E] package:pub/src/command.dart $LINE:$COL PubCommand.run.<fn> -[E] package:pub/src/command.dart $LINE:$COL PubCommand.run.<fn> [E] dart:async new Future.sync [E] package:pub/src/utils.dart $LINE:$COL captureErrors.wrappedCallback [E] dart:async runZonedGuarded @@ -289,7 +288,6 @@ FINE: Exception type: StateError ERR : tool/test-bin/pub_command_runner.dart $LINE:$COL ThrowingCommand.runProtected | package:pub/src/command.dart $LINE:$COL PubCommand.run.<fn> - | package:pub/src/command.dart $LINE:$COL PubCommand.run.<fn> | dart:async new Future.sync | package:pub/src/utils.dart $LINE:$COL captureErrors.wrappedCallback | dart:async runZonedGuarded
diff --git a/test/testdata/goldens/help_test/pub outdated --help.txt b/test/testdata/goldens/help_test/pub outdated --help.txt index 9a7b1bc..d0f6bbe 100644 --- a/test/testdata/goldens/help_test/pub outdated --help.txt +++ b/test/testdata/goldens/help_test/pub outdated --help.txt
@@ -6,9 +6,6 @@ Usage: pub outdated [options] -h, --help Print this usage information. - --[no-]color Whether to color the output. - Defaults to color when connected to a - terminal, and no-color otherwise. --[no-]dependency-overrides Show resolutions with `dependency_overrides`. (defaults to on) --[no-]dev-dependencies Take dev dependencies into account.
diff --git a/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt b/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt index 6ab6163..e9ae621 100644 --- a/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt +++ b/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt
@@ -6,9 +6,6 @@ [STDERR] [STDERR] Usage: pub outdated [options] [STDERR] -h, --help Print this usage information. -[STDERR] --[no-]color Whether to color the output. -[STDERR] Defaults to color when connected to a -[STDERR] terminal, and no-color otherwise. [STDERR] --[no-]dependency-overrides Show resolutions with `dependency_overrides`. [STDERR] (defaults to on) [STDERR] --[no-]dev-dependencies Take dev dependencies into account. @@ -38,9 +35,6 @@ [STDERR] [STDERR] Usage: pub outdated [options] [STDERR] -h, --help Print this usage information. -[STDERR] --[no-]color Whether to color the output. -[STDERR] Defaults to color when connected to a -[STDERR] terminal, and no-color otherwise. [STDERR] --[no-]dependency-overrides Show resolutions with `dependency_overrides`. [STDERR] (defaults to on) [STDERR] --[no-]dev-dependencies Take dev dependencies into account.