Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 3b15d6a

Browse files
authored
Removes retries from "dart pub get" and un-buffers its stdout/stderr output (#115801)
* Removes retries from "pub get" and proxies its stdout output * Fix issue where ErrorHandlingProcessManager does not forward "mode" parameter to backing ProcessManager's "start" method * Make "pub get" use ProcessStartMode.inheritStdio instead of forwarding bytes to stdout and stderr * Fix tests * Remove unused env var * Add back 'Running "flutter pub get"...' status log * Fix indent * Add Pub.test() constructor which lets tests mock stdio
1 parent 49f5980 commit 3b15d6a

File tree

10 files changed

+235
-447
lines changed

10 files changed

+235
-447
lines changed

packages/flutter_tools/lib/src/base/error_handling_io.dart

+1
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,7 @@ class ErrorHandlingProcessManager extends ProcessManager {
699699
environment: environment,
700700
includeParentEnvironment: includeParentEnvironment,
701701
runInShell: runInShell,
702+
mode: mode,
702703
);
703704
}, platform: _platform);
704705
}

packages/flutter_tools/lib/src/base/io.dart

-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ class Stdio {
267267
}
268268
io.Stdout? _stdout;
269269

270-
@visibleForTesting
271270
io.IOSink get stderr {
272271
if (_stderr != null) {
273272
return _stderr!;

packages/flutter_tools/lib/src/base/process.dart

+3
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ abstract class ProcessUtils {
197197
String? workingDirectory,
198198
bool allowReentrantFlutter = false,
199199
Map<String, String>? environment,
200+
ProcessStartMode mode = ProcessStartMode.normal,
200201
});
201202

202203
/// This runs the command and streams stdout/stderr from the child process to
@@ -422,12 +423,14 @@ class _DefaultProcessUtils implements ProcessUtils {
422423
String? workingDirectory,
423424
bool allowReentrantFlutter = false,
424425
Map<String, String>? environment,
426+
ProcessStartMode mode = ProcessStartMode.normal,
425427
}) {
426428
_traceCommand(cmd, workingDirectory: workingDirectory);
427429
return _processManager.start(
428430
cmd,
429431
workingDirectory: workingDirectory,
430432
environment: _environment(allowReentrantFlutter, environment),
433+
mode: mode,
431434
);
432435
}
433436

packages/flutter_tools/lib/src/context_runner.dart

-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@ Future<T> runInContext<T>(
311311
botDetector: globals.botDetector,
312312
platform: globals.platform,
313313
usage: globals.flutterUsage,
314-
stdio: globals.stdio,
315314
),
316315
Stdio: () => Stdio(),
317316
SystemClock: () => const SystemClock(),

packages/flutter_tools/lib/src/dart/pub.dart

+69-83
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:meta/meta.dart';
78
import 'package:package_config/package_config.dart';
89
import 'package:process/process.dart';
910

@@ -31,10 +32,6 @@ const String _kPubEnvironmentKey = 'PUB_ENVIRONMENT';
3132
/// The console environment key used by the pub tool to find the cache directory.
3233
const String _kPubCacheEnvironmentKey = 'PUB_CACHE';
3334

34-
/// The UNAVAILABLE exit code returned by the pub tool.
35-
/// (see https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart)
36-
const int _kPubExitCodeUnavailable = 69;
37-
3835
typedef MessageFilter = String? Function(String message);
3936

4037
/// globalCachePath is the directory in which the content of the localCachePath will be moved in
@@ -150,9 +147,20 @@ abstract class Pub {
150147
required Platform platform,
151148
required BotDetector botDetector,
152149
required Usage usage,
153-
required Stdio stdio,
154150
}) = _DefaultPub;
155151

152+
/// Create a [Pub] instance with a mocked [stdio].
153+
@visibleForTesting
154+
factory Pub.test({
155+
required FileSystem fileSystem,
156+
required Logger logger,
157+
required ProcessManager processManager,
158+
required Platform platform,
159+
required BotDetector botDetector,
160+
required Usage usage,
161+
required Stdio stdio,
162+
}) = _DefaultPub.test;
163+
156164
/// Runs `pub get` or `pub upgrade` for [project].
157165
///
158166
/// [context] provides extra information to package server requests to
@@ -214,6 +222,26 @@ class _DefaultPub implements Pub {
214222
required Platform platform,
215223
required BotDetector botDetector,
216224
required Usage usage,
225+
}) : _fileSystem = fileSystem,
226+
_logger = logger,
227+
_platform = platform,
228+
_botDetector = botDetector,
229+
_usage = usage,
230+
_processUtils = ProcessUtils(
231+
logger: logger,
232+
processManager: processManager,
233+
),
234+
_processManager = processManager,
235+
_stdio = null;
236+
237+
@visibleForTesting
238+
_DefaultPub.test({
239+
required FileSystem fileSystem,
240+
required Logger logger,
241+
required ProcessManager processManager,
242+
required Platform platform,
243+
required BotDetector botDetector,
244+
required Usage usage,
217245
required Stdio stdio,
218246
}) : _fileSystem = fileSystem,
219247
_logger = logger,
@@ -234,7 +262,7 @@ class _DefaultPub implements Pub {
234262
final BotDetector _botDetector;
235263
final Usage _usage;
236264
final ProcessManager _processManager;
237-
final Stdio _stdio;
265+
final Stdio? _stdio;
238266

239267
@override
240268
Future<void> get({
@@ -315,7 +343,7 @@ class _DefaultPub implements Pub {
315343
'--offline',
316344
'--example',
317345
];
318-
await _runWithRetries(
346+
await _runWithStdioInherited(
319347
args,
320348
command: command,
321349
context: context,
@@ -346,15 +374,15 @@ class _DefaultPub implements Pub {
346374
}
347375
}
348376

349-
/// Runs pub with [arguments].
377+
/// Runs pub with [arguments] and [ProcessStartMode.inheritStdio] mode.
350378
///
351-
/// Retries the command as long as the exit code is
352-
/// `_kPubExitCodeUnavailable`.
379+
/// Uses [ProcessStartMode.normal] and [Pub._stdio] if [Pub.test] constructor
380+
/// was used.
353381
///
354-
/// Prints the stderr and stdout of the last run.
382+
/// Prints the stdout and stderr of the whole run.
355383
///
356-
/// Sends an analytics event
357-
Future<void> _runWithRetries(
384+
/// Sends an analytics event.
385+
Future<void> _runWithStdioInherited(
358386
List<String> arguments, {
359387
required String command,
360388
required bool printProgress,
@@ -365,57 +393,47 @@ class _DefaultPub implements Pub {
365393
String? flutterRootOverride,
366394
}) async {
367395
int exitCode;
368-
int attempts = 0;
369-
int duration = 1;
370-
371-
List<_OutputLine>? output;
372-
StreamSubscription<String> recordLines(Stream<List<int>> stream, _OutputStream streamName) {
373-
return stream
374-
.transform<String>(utf8.decoder)
375-
.transform<String>(const LineSplitter())
376-
.listen((String line) => output!.add(_OutputLine(line, streamName)));
377-
}
378396

379-
final Status? status = printProgress
380-
? _logger.startProgress('Running "flutter pub $command" in ${_fileSystem.path.basename(directory)}...',)
381-
: null;
397+
_logger.printStatus('Running "flutter pub $command" in ${_fileSystem.path.basename(directory)}...');
382398
final List<String> pubCommand = _pubCommand(arguments);
383399
final Map<String, String> pubEnvironment = await _createPubEnvironment(context, flutterRootOverride);
384400
try {
385-
do {
386-
output = <_OutputLine>[];
387-
attempts += 1;
388-
final io.Process process = await _processUtils.start(
401+
final io.Process process;
402+
final io.Stdio? stdio = _stdio;
403+
404+
if (stdio != null) {
405+
// Omit mode parameter and direct pub output to [Pub._stdio] for tests.
406+
process = await _processUtils.start(
389407
pubCommand,
390408
workingDirectory: _fileSystem.path.current,
391409
environment: pubEnvironment,
392410
);
393-
final StreamSubscription<String> stdoutSubscription =
394-
recordLines(process.stdout, _OutputStream.stdout);
395-
final StreamSubscription<String> stderrSubscription =
396-
recordLines(process.stderr, _OutputStream.stderr);
397411

398-
exitCode = await process.exitCode;
412+
final StreamSubscription<List<int>> stdoutSubscription =
413+
process.stdout.listen(stdio.stdout.add);
414+
final StreamSubscription<List<int>> stderrSubscription =
415+
process.stderr.listen(stdio.stderr.add);
416+
417+
await Future.wait<void>(<Future<void>>[
418+
stdoutSubscription.asFuture<void>(),
419+
stderrSubscription.asFuture<void>(),
420+
]);
421+
399422
unawaited(stdoutSubscription.cancel());
400423
unawaited(stderrSubscription.cancel());
424+
} else {
425+
// Let pub inherit stdio for normal operation.
426+
process = await _processUtils.start(
427+
pubCommand,
428+
workingDirectory: _fileSystem.path.current,
429+
environment: pubEnvironment,
430+
mode: ProcessStartMode.inheritStdio,
431+
);
432+
}
401433

402-
if (retry && exitCode == _kPubExitCodeUnavailable) {
403-
_logger.printStatus(
404-
'$failureMessage (server unavailable) -- attempting retry $attempts in $duration '
405-
'second${ duration == 1 ? "" : "s"}...',
406-
);
407-
await Future<void>.delayed(Duration(seconds: duration));
408-
if (duration < 64) {
409-
duration *= 2;
410-
}
411-
// This will cause a retry.
412-
output = null;
413-
}
414-
} while (output == null);
415-
status?.stop();
434+
exitCode = await process.exitCode;
416435
// The exception is rethrown, so don't catch only Exceptions.
417436
} catch (exception) { // ignore: avoid_catches_without_on_clauses
418-
status?.cancel();
419437
if (exception is io.ProcessException) {
420438
final StringBuffer buffer = StringBuffer('${exception.message}\n');
421439
final String directoryExistsMessage = _fileSystem.directory(directory).existsSync()
@@ -434,40 +452,19 @@ class _DefaultPub implements Pub {
434452
rethrow;
435453
}
436454

437-
if (printProgress) {
438-
// Show the output of the last run.
439-
for (final _OutputLine line in output) {
440-
switch (line.stream) {
441-
case _OutputStream.stdout:
442-
_stdio.stdoutWrite('${line.line}\n');
443-
break;
444-
case _OutputStream.stderr:
445-
_stdio.stderrWrite('${line.line}\n');
446-
break;
447-
}
448-
}
449-
}
450-
451455
final int code = exitCode;
452-
String result = 'success';
453-
if (output.any((_OutputLine line) => line.line.contains('version solving failed'))) {
454-
result = 'version-solving-failed';
455-
} else if (code != 0) {
456-
result = 'failure';
457-
}
456+
final String result = code == 0 ? 'success' : 'failure';
458457
PubResultEvent(
459458
context: context.toAnalyticsString(),
460459
result: result,
461460
usage: _usage,
462461
).send();
463-
final String lastPubMessage = output.isEmpty ? 'no message' : output.last.line;
464462

465463
if (code != 0) {
466464
final StringBuffer buffer = StringBuffer('$failureMessage\n');
467465
buffer.writeln('command: "${pubCommand.join(' ')}"');
468466
buffer.write(_stringifyPubEnv(pubEnvironment));
469467
buffer.writeln('exit code: $code');
470-
buffer.writeln('last line of pub output: "${lastPubMessage.trim()}"');
471468
throwToolExit(
472469
buffer.toString(),
473470
exitCode: code,
@@ -813,14 +810,3 @@ class _DefaultPub implements Pub {
813810
return buffer.toString();
814811
}
815812
}
816-
817-
class _OutputLine {
818-
_OutputLine(this.line, this.stream);
819-
final String line;
820-
final _OutputStream stream;
821-
}
822-
823-
enum _OutputStream {
824-
stdout,
825-
stderr,
826-
}

packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void main() {
7777
testUsingContext('AnalysisServer success', () async {
7878
createSampleProject(tempDir);
7979

80-
final Pub pub = Pub(
80+
final Pub pub = Pub.test(
8181
fileSystem: fileSystem,
8282
logger: logger,
8383
processManager: processManager,
@@ -117,7 +117,7 @@ void main() {
117117
testUsingContext('AnalysisServer errors', () async {
118118
createSampleProject(tempDir, brokenCode: true);
119119

120-
final Pub pub = Pub(
120+
final Pub pub = Pub.test(
121121
fileSystem: fileSystem,
122122
logger: logger,
123123
processManager: processManager,

0 commit comments

Comments
 (0)