Skip to content

Commit 799b62c

Browse files
authored
Added Tooling For AGP Bulk Update (#10394)
Added AGP tooling for bulk updating agp versions in examples plugins. ## Pre-Review Checklist **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 581ce96 commit 799b62c

File tree

2 files changed

+180
-27
lines changed

2 files changed

+180
-27
lines changed

script/tool/lib/src/update_dependency_command.dart

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,19 @@ class UpdateDependencyCommand extends PackageLoopingCommand {
4343
argParser.addOption(_androidDependency,
4444
help: 'An Android dependency to update.',
4545
allowed: <String>[
46-
_AndroidDepdencyType.gradle,
47-
_AndroidDepdencyType.compileSdk,
48-
_AndroidDepdencyType.compileSdkForExamples,
46+
_AndroidDependencyType.gradle,
47+
_AndroidDependencyType.androidGradlePlugin,
48+
_AndroidDependencyType.compileSdk,
49+
_AndroidDependencyType.compileSdkForExamples,
4950
],
5051
allowedHelp: <String, String>{
51-
_AndroidDepdencyType.gradle:
52+
_AndroidDependencyType.gradle:
5253
'Updates Gradle version used in plugin example apps.',
53-
_AndroidDepdencyType.compileSdk:
54+
_AndroidDependencyType.androidGradlePlugin:
55+
'Updates AGP version used in plugin example apps.',
56+
_AndroidDependencyType.compileSdk:
5457
'Updates compileSdk version used to compile plugins.',
55-
_AndroidDepdencyType.compileSdkForExamples:
58+
_AndroidDependencyType.compileSdkForExamples:
5659
'Updates compileSdk version used to compile plugin examples.',
5760
});
5861
argParser.addOption(
@@ -137,22 +140,25 @@ ${response.httpResponse.body}
137140
if (version == null) {
138141
printError('A version must be provided to update this dependency.');
139142
throw ToolExit(_exitNoTargetVersion);
140-
} else if (_targetAndroidDependency == _AndroidDepdencyType.gradle) {
141-
final RegExp validGradleVersionPattern =
143+
} else if (_targetAndroidDependency == _AndroidDependencyType.gradle ||
144+
_targetAndroidDependency ==
145+
_AndroidDependencyType.androidGradlePlugin) {
146+
final RegExp validGradleAGPVersionPattern =
142147
RegExp(r'^\d{1,2}\.\d{1,2}(?:\.\d)?$');
143-
final bool isValidGradleVersion =
144-
validGradleVersionPattern.stringMatch(version) == version;
145-
if (!isValidGradleVersion) {
148+
final bool isValidGradleAGPVersion =
149+
validGradleAGPVersionPattern.stringMatch(version) == version;
150+
if (!isValidGradleAGPVersion) {
146151
printError('''
147152
A version with a valid format (maximum 2-3 numbers separated by 1-2 periods) must be provided.
148153
1. The first number must have one or two digits
149154
2. The second number must have one or two digits
150155
3. If present, the third number must have a single digit''');
151156
throw ToolExit(_exitInvalidTargetVersion);
152157
}
153-
} else if (_targetAndroidDependency == _AndroidDepdencyType.compileSdk ||
158+
} else if (_targetAndroidDependency ==
159+
_AndroidDependencyType.compileSdk ||
154160
_targetAndroidDependency ==
155-
_AndroidDepdencyType.compileSdkForExamples) {
161+
_AndroidDependencyType.compileSdkForExamples) {
156162
final RegExp validSdkVersion = RegExp(r'^\d{1,2}$');
157163
final bool isValidSdkVersion =
158164
validSdkVersion.stringMatch(version) == version;
@@ -254,11 +260,13 @@ A version with a valid format (maximum 2-3 numbers separated by 1-2 periods) mus
254260
/// an Android dependency.
255261
Future<PackageResult> _runForAndroidDependency(
256262
RepositoryPackage package) async {
257-
if (_targetAndroidDependency == _AndroidDepdencyType.compileSdk) {
263+
if (_targetAndroidDependency == _AndroidDependencyType.compileSdk) {
258264
return _runForCompileSdkVersion(package);
259-
} else if (_targetAndroidDependency == _AndroidDepdencyType.gradle ||
265+
} else if (_targetAndroidDependency == _AndroidDependencyType.gradle ||
260266
_targetAndroidDependency ==
261-
_AndroidDepdencyType.compileSdkForExamples) {
267+
_AndroidDependencyType.compileSdkForExamples ||
268+
_targetAndroidDependency ==
269+
_AndroidDependencyType.androidGradlePlugin) {
262270
return _runForAndroidDependencyOnExamples(package);
263271
}
264272

@@ -283,7 +291,7 @@ A version with a valid format (maximum 2-3 numbers separated by 1-2 periods) mus
283291
final RegExp dependencyVersionPattern;
284292
final String newDependencyVersionEntry;
285293

286-
if (_targetAndroidDependency == _AndroidDepdencyType.gradle) {
294+
if (_targetAndroidDependency == _AndroidDependencyType.gradle) {
287295
if (androidDirectory
288296
.childDirectory('gradle')
289297
.childDirectory('wrapper')
@@ -311,12 +319,22 @@ A version with a valid format (maximum 2-3 numbers separated by 1-2 periods) mus
311319
newDependencyVersionEntry =
312320
'distributionUrl=https\\://services.gradle.org/distributions/gradle-$_targetVersion-all.zip';
313321
} else if (_targetAndroidDependency ==
314-
_AndroidDepdencyType.compileSdkForExamples) {
322+
_AndroidDependencyType.compileSdkForExamples) {
315323
filesToUpdate.add(
316324
androidDirectory.childDirectory('app').childFile('build.gradle'));
317325
dependencyVersionPattern = RegExp(
318326
r'(compileSdk|compileSdkVersion) (\d{1,2}|flutter.compileSdkVersion)');
319327
newDependencyVersionEntry = 'compileSdk $_targetVersion';
328+
} else if (_targetAndroidDependency ==
329+
_AndroidDependencyType.androidGradlePlugin) {
330+
if (androidDirectory.childFile('settings.gradle').existsSync()) {
331+
filesToUpdate.add(androidDirectory.childFile('settings.gradle'));
332+
}
333+
dependencyVersionPattern = RegExp(
334+
r'^\s*id\s+"com\.android\.application"\s+version\s+"(\d{1,2}\.\d{1,2}(?:\.\d)?)"\s+apply\s+false\s*$',
335+
multiLine: true);
336+
newDependencyVersionEntry =
337+
' id "com.android.application" version "$_targetVersion" apply false';
320338
} else {
321339
printError(
322340
'Target Android dependency $_targetAndroidDependency is unrecognized.');
@@ -504,8 +522,9 @@ class _PubDependencyInfo {
504522

505523
enum _PubDependencyType { normal, dev }
506524

507-
class _AndroidDepdencyType {
525+
class _AndroidDependencyType {
508526
static const String gradle = 'gradle';
527+
static const String androidGradlePlugin = 'androidGradlePlugin';
509528
static const String compileSdk = 'compileSdk';
510529
static const String compileSdkForExamples = 'compileSdkForExamples';
511530
}

script/tool/test/update_dependency_command_test.dart

Lines changed: 142 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -607,15 +607,15 @@ dev_dependencies:
607607
});
608608

609609
group('Android dependencies', () {
610-
group('gradle', () {
611-
final List<String> invalidGradleVersionsFormat = <String>[
612-
'81',
613-
'811.1',
614-
'8.123',
615-
'8.12.12'
616-
];
610+
final List<String> invalidGradleAgpVersionsFormat = <String>[
611+
'81',
612+
'811.1',
613+
'8.123',
614+
'8.12.12'
615+
];
617616

618-
for (final String gradleVersion in invalidGradleVersionsFormat) {
617+
group('gradle', () {
618+
for (final String gradleVersion in invalidGradleAgpVersionsFormat) {
619619
test('throws because gradleVersion: $gradleVersion is invalid',
620620
() async {
621621
Error? commandError;
@@ -907,6 +907,140 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
907907
'gradle-$newGradleVersion-all.zip'));
908908
});
909909
});
910+
group('agp', () {
911+
for (final String agpVersion in invalidGradleAgpVersionsFormat) {
912+
test('throws because agpVersion: $agpVersion is invalid', () async {
913+
Error? commandError;
914+
final List<String> output = await runCapturingPrint(runner, <String>[
915+
'update-dependency',
916+
'--android-dependency',
917+
'androidGradlePlugin',
918+
'--version',
919+
agpVersion,
920+
], errorHandler: (Error e) {
921+
commandError = e;
922+
});
923+
924+
expect(commandError, isA<ToolExit>());
925+
expect(
926+
output,
927+
containsAllInOrder(<Matcher>[
928+
contains('''
929+
A version with a valid format (maximum 2-3 numbers separated by 1-2 periods) must be provided.
930+
1. The first number must have one or two digits
931+
2. The second number must have one or two digits
932+
3. If present, the third number must have a single digit'''),
933+
]),
934+
);
935+
});
936+
}
937+
938+
test('skips if example app does not run on Android', () async {
939+
final RepositoryPackage package =
940+
createFakePlugin('fake_plugin', packagesDir);
941+
942+
final List<String> output = await runCapturingPrint(runner, <String>[
943+
'update-dependency',
944+
'--packages',
945+
package.displayName,
946+
'--android-dependency',
947+
'androidGradlePlugin',
948+
'--version',
949+
'8.11.1',
950+
]);
951+
952+
expect(
953+
output,
954+
containsAllInOrder(<Matcher>[
955+
contains('SKIPPING: No example apps run on Android.'),
956+
]),
957+
);
958+
});
959+
960+
test('succeeds if example app has android/settings.gradle structure',
961+
() async {
962+
final RepositoryPackage package = createFakePlugin(
963+
'fake_plugin', packagesDir,
964+
extraFiles: <String>['example/android/settings.gradle']);
965+
const String newAgpVersion = '9.9';
966+
967+
final File gradleSettingsFile = package.directory
968+
.childDirectory('example')
969+
.childDirectory('android')
970+
.childFile('settings.gradle');
971+
972+
gradleSettingsFile.writeAsStringSync(r'''
973+
...
974+
plugins {
975+
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
976+
id "com.android.application" version "8.11.1" apply false
977+
...
978+
}
979+
...
980+
''');
981+
982+
await runCapturingPrint(runner, <String>[
983+
'update-dependency',
984+
'--packages',
985+
package.displayName,
986+
'--android-dependency',
987+
'androidGradlePlugin',
988+
'--version',
989+
newAgpVersion,
990+
]);
991+
992+
final String updatedGradleSettingsContents =
993+
gradleSettingsFile.readAsStringSync();
994+
995+
expect(
996+
updatedGradleSettingsContents,
997+
contains(r' id "com.android.application" version '
998+
'"$newAgpVersion" apply false'));
999+
});
1000+
1001+
test('succeeds if one example app runs on Android and another does not',
1002+
() async {
1003+
final RepositoryPackage package = createFakePlugin(
1004+
'fake_plugin', packagesDir,
1005+
examples: <String>['example_1', 'example_2'],
1006+
extraFiles: <String>['example/example_2/android/settings.gradle']);
1007+
const String newAgpVersion = '9.9';
1008+
1009+
final File gradleSettingsFile = package.directory
1010+
.childDirectory('example')
1011+
.childDirectory('example_2')
1012+
.childDirectory('android')
1013+
.childFile('settings.gradle');
1014+
1015+
gradleSettingsFile.writeAsStringSync(r'''
1016+
...
1017+
plugins {
1018+
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
1019+
id "com.android.application" version "8.11.1" apply false
1020+
...
1021+
}
1022+
...
1023+
''');
1024+
1025+
await runCapturingPrint(runner, <String>[
1026+
'update-dependency',
1027+
'--packages',
1028+
package.displayName,
1029+
'--android-dependency',
1030+
'androidGradlePlugin',
1031+
'--version',
1032+
newAgpVersion,
1033+
]);
1034+
1035+
final String updatedGradleSettingsContents =
1036+
gradleSettingsFile.readAsStringSync();
1037+
1038+
expect(
1039+
updatedGradleSettingsContents,
1040+
contains(r' id "com.android.application" version '
1041+
'"$newAgpVersion" apply false'));
1042+
});
1043+
});
9101044

9111045
group('compileSdk/compileSdkForExamples', () {
9121046
// Tests if the compileSdk version is updated for the provided

0 commit comments

Comments
 (0)