Skip to content

Commit 975e04b

Browse files
author
Emmanuel Garcia
authored
Move corrupted zip to a separate handler and ask user (#105054)
1 parent d4a9631 commit 975e04b

File tree

2 files changed

+182
-54
lines changed

2 files changed

+182
-54
lines changed

packages/flutter_tools/lib/src/android/gradle_errors.dart

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ final List<GradleHandledError> gradleErrors = <GradleHandledError>[
8181
jvm11RequiredHandler,
8282
outdatedGradleHandler,
8383
sslExceptionHandler,
84+
zipExceptionHandler,
8485
];
8586

8687
const String _boxTitle = 'Flutter Fix';
@@ -206,12 +207,6 @@ final GradleHandledError permissionDeniedErrorHandler = GradleHandledError(
206207

207208
/// Gradle crashes for several known reasons when downloading that are not
208209
/// actionable by Flutter.
209-
///
210-
/// The Gradle cache directory must be deleted, otherwise it may attempt to
211-
/// re-use the bad zip file.
212-
///
213-
/// See also:
214-
/// * https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home
215210
@visibleForTesting
216211
final GradleHandledError networkErrorHandler = GradleHandledError(
217212
test: _lineMatcher(const <String>[
@@ -234,18 +229,71 @@ final GradleHandledError networkErrorHandler = GradleHandledError(
234229
'${globals.logger.terminal.warningMark} '
235230
'Gradle threw an error while downloading artifacts from the network.'
236231
);
237-
try {
232+
return GradleBuildStatus.retry;
233+
},
234+
eventLabel: 'network',
235+
);
236+
237+
/// Handles corrupted jar or other types of zip files.
238+
///
239+
/// If a terminal is attached, this handler prompts the user if they would like to
240+
/// delete the $HOME/.gradle directory prior to retrying the build.
241+
///
242+
/// If this handler runs on a bot (e.g. a CI bot), the $HOME/.gradle is automatically deleted.
243+
///
244+
/// See also:
245+
/// * https://github.com/flutter/flutter/issues/51195
246+
/// * https://github.com/flutter/flutter/issues/89959
247+
/// * https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home
248+
@visibleForTesting
249+
final GradleHandledError zipExceptionHandler = GradleHandledError(
250+
test: _lineMatcher(const <String>[
251+
'java.util.zip.ZipException: error in opening zip file',
252+
]),
253+
handler: ({
254+
required String line,
255+
required FlutterProject project,
256+
required bool usesAndroidX,
257+
required bool multidexEnabled,
258+
}) async {
259+
globals.printError(
260+
'${globals.logger.terminal.warningMark} '
261+
'Your .gradle directory under the home directory might be corrupted.'
262+
);
263+
bool shouldDeleteUserGradle = await globals.botDetector.isRunningOnBot;
264+
if (!shouldDeleteUserGradle && globals.terminal.stdinHasTerminal) {
265+
try {
266+
final String selection = await globals.terminal.promptForCharInput(
267+
<String>['y', 'n'],
268+
logger: globals.logger,
269+
prompt: 'Do you want to delete the .gradle directory under the home directory?',
270+
defaultChoiceIndex: 0,
271+
);
272+
shouldDeleteUserGradle = selection == 'y';
273+
} on StateError catch(e) {
274+
globals.printError(
275+
e.message,
276+
indent: 0,
277+
);
278+
}
279+
}
280+
if (shouldDeleteUserGradle) {
238281
final String? homeDir = globals.platform.environment['HOME'];
239-
if (homeDir != null) {
240-
final Directory directory = globals.fs.directory(globals.fs.path.join(homeDir, '.gradle'));
241-
ErrorHandlingFileSystem.deleteIfExists(directory, recursive: true);
282+
if (homeDir == null) {
283+
globals.logger.printStatus("Could not delete .gradle directory because there isn't a HOME env variable");
284+
return GradleBuildStatus.retry;
285+
}
286+
final Directory userGradle = globals.fs.directory(globals.fs.path.join(homeDir, '.gradle'));
287+
globals.logger.printStatus('Deleting ${userGradle.path}');
288+
try {
289+
ErrorHandlingFileSystem.deleteIfExists(userGradle, recursive: true);
290+
} on FileSystemException catch (err) {
291+
globals.printTrace('Failed to delete Gradle cache: $err');
242292
}
243-
} on FileSystemException catch (err) {
244-
globals.printTrace('Failed to delete Gradle cache: $err');
245293
}
246294
return GradleBuildStatus.retry;
247295
},
248-
eventLabel: 'network',
296+
eventLabel: 'zip-exception',
249297
);
250298

251299
// R8 failure.

packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart

Lines changed: 121 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:file/memory.dart';
88
import 'package:file_testing/file_testing.dart';
99
import 'package:flutter_tools/src/android/gradle_errors.dart';
1010
import 'package:flutter_tools/src/android/gradle_utils.dart';
11+
import 'package:flutter_tools/src/base/bot_detector.dart';
1112
import 'package:flutter_tools/src/base/file_system.dart';
1213
import 'package:flutter_tools/src/base/logger.dart';
1314
import 'package:flutter_tools/src/base/platform.dart';
@@ -17,6 +18,7 @@ import 'package:flutter_tools/src/project.dart';
1718

1819
import '../../src/common.dart';
1920
import '../../src/context.dart';
21+
import '../../src/fakes.dart';
2022

2123
void main() {
2224
group('gradleErrors', () {
@@ -38,53 +40,13 @@ void main() {
3840
jvm11RequiredHandler,
3941
outdatedGradleHandler,
4042
sslExceptionHandler,
43+
zipExceptionHandler,
4144
])
4245
);
4346
});
4447
});
4548

4649
group('network errors', () {
47-
testUsingContext('retries and deletes zip if gradle fails to unzip', () async {
48-
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
49-
const String errorMessage = r'''
50-
Exception in thread "main" java.util.zip.ZipException: error in opening zip file
51-
at java.util.zip.ZipFile.open(Native Method)
52-
at java.util.zip.ZipFile.(ZipFile.java:225)
53-
at java.util.zip.ZipFile.(ZipFile.java:155)
54-
at java.util.zip.ZipFile.(ZipFile.java:169)
55-
at org.gradle.wrapper.Install.unzip(Install.java:214)
56-
at org.gradle.wrapper.Install.access$600(Install.java:27)
57-
at org.gradle.wrapper.Install$1.call(Install.java:74)
58-
at org.gradle.wrapper.Install$1.call(Install.java:48)
59-
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
60-
at org.gradle.wrapper.Install.createDist(Install.java:48)
61-
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
62-
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
63-
[!] Gradle threw an error while trying to update itself. Retrying the update...
64-
Exception in thread "main" java.util.zip.ZipException: error in opening zip file
65-
at java.util.zip.ZipFile.open(Native Method)
66-
at java.util.zip.ZipFile.(ZipFile.java:225)
67-
at java.util.zip.ZipFile.(ZipFile.java:155)
68-
at java.util.zip.ZipFile.(ZipFile.java:169)
69-
at org.gradle.wrapper.Install.unzip(Install.java:214)
70-
at org.gradle.wrapper.Install.access$600(Install.java:27)
71-
at org.gradle.wrapper.Install$1.call(Install.java:74)
72-
at org.gradle.wrapper.Install$1.call(Install.java:48)
73-
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
74-
at org.gradle.wrapper.Install.createDist(Install.java:48)
75-
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
76-
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
77-
''';
78-
79-
expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
80-
expect(await networkErrorHandler.handler(), equals(GradleBuildStatus.retry));
81-
expect(globals.fs.file('foo/.gradle/fizz.zip'), isNot(exists));
82-
}, overrides: <Type, Generator>{
83-
FileSystem: () => MemoryFileSystem.test(),
84-
ProcessManager: () => FakeProcessManager.any(),
85-
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
86-
});
87-
8850
testUsingContext('retries if gradle fails while downloading', () async {
8951
const String errorMessage = r'''
9052
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)'''
11221084
});
11231085
});
11241086

1087+
group('Zip exception', () {
1088+
testWithoutContext('pattern', () {
1089+
expect(
1090+
zipExceptionHandler.test(r'''
1091+
Exception in thread "main" java.util.zip.ZipException: error in opening zip file
1092+
at java.util.zip.ZipFile.open(Native Method)
1093+
at java.util.zip.ZipFile.(ZipFile.java:225)
1094+
at java.util.zip.ZipFile.(ZipFile.java:155)
1095+
at java.util.zip.ZipFile.(ZipFile.java:169)
1096+
at org.gradle.wrapper.Install.unzip(Install.java:214)
1097+
at org.gradle.wrapper.Install.access$600(Install.java:27)
1098+
at org.gradle.wrapper.Install$1.call(Install.java:74)
1099+
at org.gradle.wrapper.Install$1.call(Install.java:48)
1100+
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
1101+
at org.gradle.wrapper.Install.createDist(Install.java:48)
1102+
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
1103+
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''
1104+
),
1105+
isTrue,
1106+
);
1107+
});
1108+
1109+
testUsingContext('suggestion', () async {
1110+
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1111+
1112+
final GradleBuildStatus result = await zipExceptionHandler.handler();
1113+
1114+
expect(result, equals(GradleBuildStatus.retry));
1115+
expect(globals.fs.file('foo/.gradle/fizz.zip'), exists);
1116+
expect(
1117+
testLogger.errorText,
1118+
contains(
1119+
'[!] Your .gradle directory under the home directory might be corrupted.\n'
1120+
)
1121+
);
1122+
expect(testLogger.statusText, '');
1123+
}, overrides: <Type, Generator>{
1124+
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1125+
FileSystem: () => MemoryFileSystem.test(),
1126+
ProcessManager: () => FakeProcessManager.empty(),
1127+
BotDetector: () => const FakeBotDetector(false),
1128+
});
1129+
1130+
testUsingContext('suggestion if running as bot', () async {
1131+
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1132+
1133+
final GradleBuildStatus result = await zipExceptionHandler.handler();
1134+
1135+
expect(result, equals(GradleBuildStatus.retry));
1136+
expect(globals.fs.file('foo/.gradle/fizz.zip'), isNot(exists));
1137+
1138+
expect(
1139+
testLogger.errorText,
1140+
contains(
1141+
'[!] Your .gradle directory under the home directory might be corrupted.\n'
1142+
)
1143+
);
1144+
expect(
1145+
testLogger.statusText,
1146+
contains('Deleting foo/.gradle\n'),
1147+
);
1148+
}, overrides: <Type, Generator>{
1149+
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1150+
FileSystem: () => MemoryFileSystem.test(),
1151+
ProcessManager: () => FakeProcessManager.empty(),
1152+
BotDetector: () => const FakeBotDetector(true),
1153+
});
1154+
1155+
testUsingContext('suggestion if stdin has terminal and user entered y', () async {
1156+
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1157+
1158+
final GradleBuildStatus result = await zipExceptionHandler.handler();
1159+
1160+
expect(result, equals(GradleBuildStatus.retry));
1161+
expect(globals.fs.file('foo/.gradle/fizz.zip'), isNot(exists));
1162+
expect(
1163+
testLogger.errorText,
1164+
contains(
1165+
'[!] Your .gradle directory under the home directory might be corrupted.\n'
1166+
)
1167+
);
1168+
expect(
1169+
testLogger.statusText,
1170+
contains('Deleting foo/.gradle\n'),
1171+
);
1172+
}, overrides: <Type, Generator>{
1173+
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1174+
FileSystem: () => MemoryFileSystem.test(),
1175+
ProcessManager: () => FakeProcessManager.empty(),
1176+
AnsiTerminal: () => _TestPromptTerminal('y'),
1177+
BotDetector: () => const FakeBotDetector(false),
1178+
});
1179+
1180+
testUsingContext('suggestion if stdin has terminal and user entered n', () async {
1181+
globals.fs.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1182+
1183+
final GradleBuildStatus result = await zipExceptionHandler.handler();
1184+
1185+
expect(result, equals(GradleBuildStatus.retry));
1186+
expect(globals.fs.file('foo/.gradle/fizz.zip'), exists);
1187+
expect(
1188+
testLogger.errorText,
1189+
contains(
1190+
'[!] Your .gradle directory under the home directory might be corrupted.\n'
1191+
)
1192+
);
1193+
expect(testLogger.statusText, '');
1194+
}, overrides: <Type, Generator>{
1195+
Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1196+
FileSystem: () => MemoryFileSystem.test(),
1197+
ProcessManager: () => FakeProcessManager.empty(),
1198+
AnsiTerminal: () => _TestPromptTerminal('n'),
1199+
BotDetector: () => const FakeBotDetector(false),
1200+
});
1201+
});
11251202
}
11261203

11271204
bool formatTestErrorMessage(String errorMessage, GradleHandledError error) {
@@ -1162,4 +1239,7 @@ class _TestPromptTerminal extends AnsiTerminal {
11621239
}) {
11631240
return Future<String>.value(promptResult);
11641241
}
1242+
1243+
@override
1244+
bool get stdinHasTerminal => true;
11651245
}

0 commit comments

Comments
 (0)