Unverified Commit 53be552a authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Be more verbose when pub fails (#42187)

parent 6d18d31b
...@@ -114,13 +114,13 @@ abstract class Pub { ...@@ -114,13 +114,13 @@ abstract class Pub {
/// understand usage. /// understand usage.
Future<void> batch( Future<void> batch(
List<String> arguments, { List<String> arguments, {
@required PubContext context, @required PubContext context,
String directory, String directory,
MessageFilter filter, MessageFilter filter,
String failureMessage = 'pub failed', String failureMessage = 'pub failed',
@required bool retry, @required bool retry,
bool showTraceForErrors, bool showTraceForErrors,
}); });
/// Runs pub in 'interactive' mode. /// Runs pub in 'interactive' mode.
...@@ -129,8 +129,8 @@ abstract class Pub { ...@@ -129,8 +129,8 @@ abstract class Pub {
/// stdout/stderr stream of pub to the corresponding streams of this process. /// stdout/stderr stream of pub to the corresponding streams of this process.
Future<void> interactively( Future<void> interactively(
List<String> arguments, { List<String> arguments, {
String directory, String directory,
}); });
} }
class _DefaultPub implements Pub { class _DefaultPub implements Pub {
...@@ -201,17 +201,19 @@ class _DefaultPub implements Pub { ...@@ -201,17 +201,19 @@ class _DefaultPub implements Pub {
@override @override
Future<void> batch( Future<void> batch(
List<String> arguments, { List<String> arguments, {
@required PubContext context, @required PubContext context,
String directory, String directory,
MessageFilter filter, MessageFilter filter,
String failureMessage = 'pub failed', String failureMessage = 'pub failed',
@required bool retry, @required bool retry,
bool showTraceForErrors, bool showTraceForErrors,
}) async { }) async {
showTraceForErrors ??= isRunningOnBot; showTraceForErrors ??= isRunningOnBot;
String lastPubMessage = 'no message';
bool versionSolvingFailed = false; bool versionSolvingFailed = false;
String filterWrapper(String line) { String filterWrapper(String line) {
lastPubMessage = line;
if (line.contains('version solving failed')) { if (line.contains('version solving failed')) {
versionSolvingFailed = true; versionSolvingFailed = true;
} }
...@@ -227,19 +229,25 @@ class _DefaultPub implements Pub { ...@@ -227,19 +229,25 @@ class _DefaultPub implements Pub {
int attempts = 0; int attempts = 0;
int duration = 1; int duration = 1;
int code; int code;
while (true) { loop: while (true) {
attempts += 1; attempts += 1;
code = await processUtils.stream( code = await processUtils.stream(
_pubCommand(arguments), _pubCommand(arguments),
workingDirectory: directory, workingDirectory: directory,
mapFunction: filterWrapper, mapFunction: filterWrapper, // may set versionSolvingFailed, lastPubMessage
environment: _createPubEnvironment(context), environment: _createPubEnvironment(context),
); );
if (code != 69) { // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart String message;
break; switch (code) {
case 69: // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart
message = 'server unavailable';
break;
default:
break loop;
} }
assert(message != null);
versionSolvingFailed = false; versionSolvingFailed = false;
printStatus('$failureMessage ($code) -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...'); printStatus('$failureMessage ($message) -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...');
await Future<void>.delayed(Duration(seconds: duration)); await Future<void>.delayed(Duration(seconds: duration));
if (duration < 64) { if (duration < 64) {
duration *= 2; duration *= 2;
...@@ -259,14 +267,14 @@ class _DefaultPub implements Pub { ...@@ -259,14 +267,14 @@ class _DefaultPub implements Pub {
).send(); ).send();
if (code != 0) { if (code != 0) {
throwToolExit('$failureMessage ($code)', exitCode: code); throwToolExit('$failureMessage ($code; $lastPubMessage)', exitCode: code);
} }
} }
@override @override
Future<void> interactively( Future<void> interactively(
List<String> arguments, { List<String> arguments, {
String directory, String directory,
}) async { }) async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
final io.Process process = await processUtils.start( final io.Process process = await processUtils.start(
......
...@@ -44,46 +44,46 @@ void main() { ...@@ -44,46 +44,46 @@ void main() {
time.elapse(const Duration(milliseconds: 500)); time.elapse(const Duration(milliseconds: 500));
expect(testLogger.statusText, expect(testLogger.statusText,
'Running "flutter pub get" in /...\n' 'Running "flutter pub get" in /...\n'
'pub get failed (69) -- attempting retry 1 in 1 second...\n', 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n',
); );
expect(processMock.lastPubEnvironment, contains('flutter_cli:flutter_tests')); expect(processMock.lastPubEnvironment, contains('flutter_cli:flutter_tests'));
expect(processMock.lastPubCache, isNull); expect(processMock.lastPubCache, isNull);
time.elapse(const Duration(milliseconds: 500)); time.elapse(const Duration(milliseconds: 500));
expect(testLogger.statusText, expect(testLogger.statusText,
'Running "flutter pub get" in /...\n' 'Running "flutter pub get" in /...\n'
'pub get failed (69) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
'pub get failed (69) -- attempting retry 2 in 2 seconds...\n', 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n',
); );
time.elapse(const Duration(seconds: 1)); time.elapse(const Duration(seconds: 1));
expect(testLogger.statusText, expect(testLogger.statusText,
'Running "flutter pub get" in /...\n' 'Running "flutter pub get" in /...\n'
'pub get failed (69) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
'pub get failed (69) -- attempting retry 2 in 2 seconds...\n', 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n',
); );
time.elapse(const Duration(seconds: 100)); // from t=0 to t=100 time.elapse(const Duration(seconds: 100)); // from t=0 to t=100
expect(testLogger.statusText, expect(testLogger.statusText,
'Running "flutter pub get" in /...\n' 'Running "flutter pub get" in /...\n'
'pub get failed (69) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
'pub get failed (69) -- attempting retry 2 in 2 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n'
'pub get failed (69) -- attempting retry 3 in 4 seconds...\n' // at t=1 'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n' // at t=1
'pub get failed (69) -- attempting retry 4 in 8 seconds...\n' // at t=5 'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n' // at t=5
'pub get failed (69) -- attempting retry 5 in 16 seconds...\n' // at t=13 'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n' // at t=13
'pub get failed (69) -- attempting retry 6 in 32 seconds...\n' // at t=29 'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n' // at t=29
'pub get failed (69) -- attempting retry 7 in 64 seconds...\n', // at t=61 'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n', // at t=61
); );
time.elapse(const Duration(seconds: 200)); // from t=0 to t=200 time.elapse(const Duration(seconds: 200)); // from t=0 to t=200
expect(testLogger.statusText, expect(testLogger.statusText,
'Running "flutter pub get" in /...\n' 'Running "flutter pub get" in /...\n'
'pub get failed (69) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
'pub get failed (69) -- attempting retry 2 in 2 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n'
'pub get failed (69) -- attempting retry 3 in 4 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n'
'pub get failed (69) -- attempting retry 4 in 8 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n'
'pub get failed (69) -- attempting retry 5 in 16 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n'
'pub get failed (69) -- attempting retry 6 in 32 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n'
'pub get failed (69) -- attempting retry 7 in 64 seconds...\n' 'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n'
'pub get failed (69) -- attempting retry 8 in 64 seconds...\n' // at t=39 'pub get failed (server unavailable) -- attempting retry 8 in 64 seconds...\n' // at t=39
'pub get failed (69) -- attempting retry 9 in 64 seconds...\n' // at t=103 'pub get failed (server unavailable) -- attempting retry 9 in 64 seconds...\n' // at t=103
'pub get failed (69) -- attempting retry 10 in 64 seconds...\n', // at t=167 'pub get failed (server unavailable) -- attempting retry 10 in 64 seconds...\n', // at t=167
); );
}); });
expect(testLogger.errorText, isEmpty); expect(testLogger.errorText, isEmpty);
...@@ -97,6 +97,33 @@ void main() { ...@@ -97,6 +97,33 @@ void main() {
Pub: () => const Pub(), Pub: () => const Pub(),
}); });
testUsingContext('pub get 66 shows message from pub', () async {
try {
await pub.get(context: PubContext.flutterTests, checkLastModified: false);
throw AssertionError('pubGet did not fail');
} on ToolExit catch (error) {
expect(error.message, 'pub get failed (66; err3)');
}
expect(testLogger.statusText,
'Running "flutter pub get" in /...\n'
'out1\n'
'out2\n'
'out3\n'
);
expect(testLogger.errorText,
'err1\n'
'err2\n'
'err3\n'
);
}, overrides: <Type, Generator>{
ProcessManager: () => MockProcessManager(66, stderr: 'err1\nerr2\nerr3\n', stdout: 'out1\nout2\nout3\n'),
FileSystem: () => MockFileSystem(),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{}),
),
Pub: () => const Pub(),
});
testUsingContext('pub cache in root is used', () async { testUsingContext('pub cache in root is used', () async {
String error; String error;
...@@ -218,10 +245,12 @@ typedef StartCallback = void Function(List<dynamic> command); ...@@ -218,10 +245,12 @@ typedef StartCallback = void Function(List<dynamic> command);
class MockProcessManager implements ProcessManager { class MockProcessManager implements ProcessManager {
MockProcessManager(this.fakeExitCode, { MockProcessManager(this.fakeExitCode, {
this.stdout = '',
this.stderr = '', this.stderr = '',
}); });
final int fakeExitCode; final int fakeExitCode;
final String stdout;
final String stderr; final String stderr;
String lastPubEnvironment; String lastPubEnvironment;
...@@ -240,6 +269,7 @@ class MockProcessManager implements ProcessManager { ...@@ -240,6 +269,7 @@ class MockProcessManager implements ProcessManager {
lastPubCache = environment['PUB_CACHE']; lastPubCache = environment['PUB_CACHE'];
return Future<Process>.value(mocks.createMockProcess( return Future<Process>.value(mocks.createMockProcess(
exitCode: fakeExitCode, exitCode: fakeExitCode,
stdout: stdout,
stderr: stderr, stderr: stderr,
)); ));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment