Move corrupted zip to a separate handler and ask user (#105054)
This commit is contained in:
@@ -81,6 +81,7 @@ final List<GradleHandledError> gradleErrors = <GradleHandledError>[
|
||||
jvm11RequiredHandler,
|
||||
outdatedGradleHandler,
|
||||
sslExceptionHandler,
|
||||
zipExceptionHandler,
|
||||
];
|
||||
|
||||
const String _boxTitle = 'Flutter Fix';
|
||||
@@ -206,12 +207,6 @@ final GradleHandledError permissionDeniedErrorHandler = GradleHandledError(
|
||||
|
||||
/// Gradle crashes for several known reasons when downloading that are not
|
||||
/// actionable by Flutter.
|
||||
///
|
||||
/// The Gradle cache directory must be deleted, otherwise it may attempt to
|
||||
/// re-use the bad zip file.
|
||||
///
|
||||
/// See also:
|
||||
/// * https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home
|
||||
@visibleForTesting
|
||||
final GradleHandledError networkErrorHandler = GradleHandledError(
|
||||
test: _lineMatcher(const <String>[
|
||||
@@ -234,20 +229,73 @@ final GradleHandledError networkErrorHandler = GradleHandledError(
|
||||
'${globals.logger.terminal.warningMark} '
|
||||
'Gradle threw an error while downloading artifacts from the network.'
|
||||
);
|
||||
try {
|
||||
final String? homeDir = globals.platform.environment['HOME'];
|
||||
if (homeDir != null) {
|
||||
final Directory directory = globals.fs.directory(globals.fs.path.join(homeDir, '.gradle'));
|
||||
ErrorHandlingFileSystem.deleteIfExists(directory, recursive: true);
|
||||
}
|
||||
} on FileSystemException catch (err) {
|
||||
globals.printTrace('Failed to delete Gradle cache: $err');
|
||||
}
|
||||
return GradleBuildStatus.retry;
|
||||
},
|
||||
eventLabel: 'network',
|
||||
);
|
||||
|
||||
/// Handles corrupted jar or other types of zip files.
|
||||
///
|
||||
/// If a terminal is attached, this handler prompts the user if they would like to
|
||||
/// delete the $HOME/.gradle directory prior to retrying the build.
|
||||
///
|
||||
/// If this handler runs on a bot (e.g. a CI bot), the $HOME/.gradle is automatically deleted.
|
||||
///
|
||||
/// See also:
|
||||
/// * https://github.com/flutter/flutter/issues/51195
|
||||
/// * https://github.com/flutter/flutter/issues/89959
|
||||
/// * https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home
|
||||
@visibleForTesting
|
||||
final GradleHandledError zipExceptionHandler = GradleHandledError(
|
||||
test: _lineMatcher(const <String>[
|
||||
'java.util.zip.ZipException: error in opening zip file',
|
||||
]),
|
||||
handler: ({
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool multidexEnabled,
|
||||
}) async {
|
||||
globals.printError(
|
||||
'${globals.logger.terminal.warningMark} '
|
||||
'Your .gradle directory under the home directory might be corrupted.'
|
||||
);
|
||||
bool shouldDeleteUserGradle = await globals.botDetector.isRunningOnBot;
|
||||
if (!shouldDeleteUserGradle && globals.terminal.stdinHasTerminal) {
|
||||
try {
|
||||
final String selection = await globals.terminal.promptForCharInput(
|
||||
<String>['y', 'n'],
|
||||
logger: globals.logger,
|
||||
prompt: 'Do you want to delete the .gradle directory under the home directory?',
|
||||
defaultChoiceIndex: 0,
|
||||
);
|
||||
shouldDeleteUserGradle = selection == 'y';
|
||||
} on StateError catch(e) {
|
||||
globals.printError(
|
||||
e.message,
|
||||
indent: 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (shouldDeleteUserGradle) {
|
||||
final String? homeDir = globals.platform.environment['HOME'];
|
||||
if (homeDir == null) {
|
||||
globals.logger.printStatus("Could not delete .gradle directory because there isn't a HOME env variable");
|
||||
return GradleBuildStatus.retry;
|
||||
}
|
||||
final Directory userGradle = globals.fs.directory(globals.fs.path.join(homeDir, '.gradle'));
|
||||
globals.logger.printStatus('Deleting ${userGradle.path}');
|
||||
try {
|
||||
ErrorHandlingFileSystem.deleteIfExists(userGradle, recursive: true);
|
||||
} on FileSystemException catch (err) {
|
||||
globals.printTrace('Failed to delete Gradle cache: $err');
|
||||
}
|
||||
}
|
||||
return GradleBuildStatus.retry;
|
||||
},
|
||||
eventLabel: 'zip-exception',
|
||||
);
|
||||
|
||||
// R8 failure.
|
||||
@visibleForTesting
|
||||
final GradleHandledError r8FailureHandler = GradleHandledError(
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:file/memory.dart';
|
||||
import 'package:file_testing/file_testing.dart';
|
||||
import 'package:flutter_tools/src/android/gradle_errors.dart';
|
||||
import 'package:flutter_tools/src/android/gradle_utils.dart';
|
||||
import 'package:flutter_tools/src/base/bot_detector.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
@@ -17,6 +18,7 @@ import 'package:flutter_tools/src/project.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fakes.dart';
|
||||
|
||||
void main() {
|
||||
group('gradleErrors', () {
|
||||
@@ -38,53 +40,13 @@ void main() {
|
||||
jvm11RequiredHandler,
|
||||
outdatedGradleHandler,
|
||||
sslExceptionHandler,
|
||||
zipExceptionHandler,
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('network errors', () {
|
||||
testUsingContext('retries and deletes zip if gradle fails to unzip', () async {
|
||||
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
|
||||
const String errorMessage = r'''
|
||||
Exception in thread "main" java.util.zip.ZipException: error in opening zip file
|
||||
at java.util.zip.ZipFile.open(Native Method)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:225)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:155)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:169)
|
||||
at org.gradle.wrapper.Install.unzip(Install.java:214)
|
||||
at org.gradle.wrapper.Install.access$600(Install.java:27)
|
||||
at org.gradle.wrapper.Install$1.call(Install.java:74)
|
||||
at org.gradle.wrapper.Install$1.call(Install.java:48)
|
||||
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
|
||||
at org.gradle.wrapper.Install.createDist(Install.java:48)
|
||||
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
|
||||
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
|
||||
[!] Gradle threw an error while trying to update itself. Retrying the update...
|
||||
Exception in thread "main" java.util.zip.ZipException: error in opening zip file
|
||||
at java.util.zip.ZipFile.open(Native Method)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:225)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:155)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:169)
|
||||
at org.gradle.wrapper.Install.unzip(Install.java:214)
|
||||
at org.gradle.wrapper.Install.access$600(Install.java:27)
|
||||
at org.gradle.wrapper.Install$1.call(Install.java:74)
|
||||
at org.gradle.wrapper.Install$1.call(Install.java:48)
|
||||
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
|
||||
at org.gradle.wrapper.Install.createDist(Install.java:48)
|
||||
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
|
||||
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
|
||||
''';
|
||||
|
||||
expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
|
||||
expect(await networkErrorHandler.handler(), equals(GradleBuildStatus.retry));
|
||||
expect(globals.fs.file('foo/.gradle/fizz.zip'), isNot(exists));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
|
||||
});
|
||||
|
||||
testUsingContext('retries if gradle fails while downloading', () async {
|
||||
const String errorMessage = r'''
|
||||
Exception in thread "main" java.io.FileNotFoundException: https://downloads.gradle.org/distributions/gradle-4.1.1-all.zip
|
||||
@@ -1122,6 +1084,121 @@ at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:108)'''
|
||||
});
|
||||
});
|
||||
|
||||
group('Zip exception', () {
|
||||
testWithoutContext('pattern', () {
|
||||
expect(
|
||||
zipExceptionHandler.test(r'''
|
||||
Exception in thread "main" java.util.zip.ZipException: error in opening zip file
|
||||
at java.util.zip.ZipFile.open(Native Method)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:225)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:155)
|
||||
at java.util.zip.ZipFile.(ZipFile.java:169)
|
||||
at org.gradle.wrapper.Install.unzip(Install.java:214)
|
||||
at org.gradle.wrapper.Install.access$600(Install.java:27)
|
||||
at org.gradle.wrapper.Install$1.call(Install.java:74)
|
||||
at org.gradle.wrapper.Install$1.call(Install.java:48)
|
||||
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
|
||||
at org.gradle.wrapper.Install.createDist(Install.java:48)
|
||||
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
|
||||
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('suggestion', () async {
|
||||
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus result = await zipExceptionHandler.handler();
|
||||
|
||||
expect(result, equals(GradleBuildStatus.retry));
|
||||
expect(globals.fs.file('foo/.gradle/fizz.zip'), exists);
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
contains(
|
||||
'[!] Your .gradle directory under the home directory might be corrupted.\n'
|
||||
)
|
||||
);
|
||||
expect(testLogger.statusText, '');
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
BotDetector: () => const FakeBotDetector(false),
|
||||
});
|
||||
|
||||
testUsingContext('suggestion if running as bot', () async {
|
||||
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus result = await zipExceptionHandler.handler();
|
||||
|
||||
expect(result, equals(GradleBuildStatus.retry));
|
||||
expect(globals.fs.file('foo/.gradle/fizz.zip'), isNot(exists));
|
||||
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
contains(
|
||||
'[!] Your .gradle directory under the home directory might be corrupted.\n'
|
||||
)
|
||||
);
|
||||
expect(
|
||||
testLogger.statusText,
|
||||
contains('Deleting foo/.gradle\n'),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
BotDetector: () => const FakeBotDetector(true),
|
||||
});
|
||||
|
||||
testUsingContext('suggestion if stdin has terminal and user entered y', () async {
|
||||
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus result = await zipExceptionHandler.handler();
|
||||
|
||||
expect(result, equals(GradleBuildStatus.retry));
|
||||
expect(globals.fs.file('foo/.gradle/fizz.zip'), isNot(exists));
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
contains(
|
||||
'[!] Your .gradle directory under the home directory might be corrupted.\n'
|
||||
)
|
||||
);
|
||||
expect(
|
||||
testLogger.statusText,
|
||||
contains('Deleting foo/.gradle\n'),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
AnsiTerminal: () => _TestPromptTerminal('y'),
|
||||
BotDetector: () => const FakeBotDetector(false),
|
||||
});
|
||||
|
||||
testUsingContext('suggestion if stdin has terminal and user entered n', () async {
|
||||
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus result = await zipExceptionHandler.handler();
|
||||
|
||||
expect(result, equals(GradleBuildStatus.retry));
|
||||
expect(globals.fs.file('foo/.gradle/fizz.zip'), exists);
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
contains(
|
||||
'[!] Your .gradle directory under the home directory might be corrupted.\n'
|
||||
)
|
||||
);
|
||||
expect(testLogger.statusText, '');
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
AnsiTerminal: () => _TestPromptTerminal('n'),
|
||||
BotDetector: () => const FakeBotDetector(false),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool formatTestErrorMessage(String errorMessage, GradleHandledError error) {
|
||||
@@ -1162,4 +1239,7 @@ class _TestPromptTerminal extends AnsiTerminal {
|
||||
}) {
|
||||
return Future<String>.value(promptResult);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get stdinHasTerminal => true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user