Skip to content

Commit aaae5ef

Browse files
authored
[tool] Add Android dependency (gradle) option to update dependencies command (flutter#4757)
Adds an `android-dependency` option to the `update-dependency` command such that you can update Android dependencies provided the dependency and a version across relevant plugins. This PR specifically adds support for the Gradle dependency, relevant to plugin example apps. Running the command looks like: ``` dart run script/tool/bin/flutter_plugin_tools.dart update-dependency --android-dependency gradle --version 1.2.3 ```
1 parent c6fe5fa commit aaae5ef

File tree

2 files changed

+353
-3
lines changed

2 files changed

+353
-3
lines changed

script/tool/lib/src/update_dependency_command.dart

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'common/repository_package.dart';
1919

2020
const int _exitIncorrectTargetDependency = 3;
2121
const int _exitNoTargetVersion = 4;
22+
const int _exitInvalidTargetVersion = 5;
2223

2324
/// A command to update a dependency in packages.
2425
///
@@ -38,23 +39,34 @@ class UpdateDependencyCommand extends PackageLoopingCommand {
3839
_pubPackageFlag,
3940
help: 'A pub package to update.',
4041
);
42+
argParser.addOption(_androidDependency,
43+
help: 'An Android dependency to update.',
44+
allowed: <String>[
45+
'gradle',
46+
],
47+
allowedHelp: <String, String>{
48+
'gradle': 'Updates Gradle version used in plugin example apps.',
49+
});
4150
argParser.addOption(
4251
_versionFlag,
4352
help: 'The version to update to.\n\n'
4453
'- For pub, defaults to the latest published version if not '
4554
'provided. This can be any constraint that pubspec.yaml allows; a '
4655
'specific version will be treated as the exact version for '
4756
'dependencies that are alread pinned, or a ^ range for those that '
48-
'are unpinned.',
57+
'are unpinned.\n'
58+
'- For Android dependencies, a version must be provided.',
4959
);
5060
}
5161

5262
static const String _pubPackageFlag = 'pub-package';
63+
static const String _androidDependency = 'android-dependency';
5364
static const String _versionFlag = 'version';
5465

5566
final PubVersionFinder _pubVersionFinder;
5667

5768
late final String? _targetPubPackage;
69+
late final String? _targetAndroidDependency;
5870
late final String _targetVersion;
5971

6072
@override
@@ -72,14 +84,19 @@ class UpdateDependencyCommand extends PackageLoopingCommand {
7284

7385
@override
7486
Future<void> initializeRun() async {
75-
const Set<String> targetFlags = <String>{_pubPackageFlag};
87+
const Set<String> targetFlags = <String>{
88+
_pubPackageFlag,
89+
_androidDependency
90+
};
7691
final Set<String> passedTargetFlags =
7792
targetFlags.where((String flag) => argResults![flag] != null).toSet();
7893
if (passedTargetFlags.length != 1) {
7994
printError(
8095
'Exactly one of the target flags must be provided: (${targetFlags.join(', ')})');
8196
throw ToolExit(_exitIncorrectTargetDependency);
8297
}
98+
99+
// Setup for updating pub dependency.
83100
_targetPubPackage = getNullableStringArg(_pubPackageFlag);
84101
if (_targetPubPackage != null) {
85102
final String? version = getNullableStringArg(_versionFlag);
@@ -102,6 +119,33 @@ ${response.httpResponse.body}
102119
}
103120
} else {
104121
_targetVersion = version;
122+
return;
123+
}
124+
}
125+
126+
// Setup for updating Android dependency.
127+
_targetAndroidDependency = getNullableStringArg(_androidDependency);
128+
if (_targetAndroidDependency != null) {
129+
final String? version = getNullableStringArg(_versionFlag);
130+
if (version == null) {
131+
printError('A version must be provided to update this dependency.');
132+
throw ToolExit(_exitNoTargetVersion);
133+
} else if (_targetAndroidDependency == 'gradle') {
134+
final RegExp validGradleVersionPattern = RegExp(r'^\d+(?:\.\d+){1,2}$');
135+
final bool isValidGradleVersion =
136+
validGradleVersionPattern.stringMatch(version) == version;
137+
if (!isValidGradleVersion) {
138+
printError(
139+
'A version with a valid format (maximum 2-3 numbers separated by period) must be provided.');
140+
throw ToolExit(_exitInvalidTargetVersion);
141+
}
142+
_targetVersion = version;
143+
return;
144+
} else {
145+
// TODO(camsim99): Add other supported Android dependencies like the Android SDK and AGP.
146+
printError(
147+
'Target Android dependency $_targetAndroidDependency is unrecognized.');
148+
throw ToolExit(_exitIncorrectTargetDependency);
105149
}
106150
}
107151
}
@@ -116,7 +160,11 @@ ${response.httpResponse.body}
116160
if (_targetPubPackage != null) {
117161
return _runForPubDependency(package, _targetPubPackage!);
118162
}
119-
// TODO(stuartmorgan): Add othe dependency types here (e.g., maven).
163+
if (_targetAndroidDependency != null) {
164+
return _runForAndroidDependency(package);
165+
}
166+
167+
// TODO(stuartmorgan): Add other dependency types here (e.g., maven).
120168

121169
return PackageResult.fail();
122170
}
@@ -181,6 +229,65 @@ ${response.httpResponse.body}
181229
return PackageResult.success();
182230
}
183231

232+
/// Handles all of the updates for [package] when the target dependency is
233+
/// an Android dependency.
234+
Future<PackageResult> _runForAndroidDependency(
235+
RepositoryPackage package) async {
236+
if (_targetAndroidDependency == 'gradle') {
237+
final Iterable<RepositoryPackage> packageExamples = package.getExamples();
238+
bool updateRanForExamples = false;
239+
for (final RepositoryPackage example in packageExamples) {
240+
if (!example.platformDirectory(FlutterPlatform.android).existsSync()) {
241+
continue;
242+
}
243+
244+
updateRanForExamples = true;
245+
Directory gradleWrapperPropertiesDirectory =
246+
example.platformDirectory(FlutterPlatform.android);
247+
if (gradleWrapperPropertiesDirectory
248+
.childDirectory('app')
249+
.childDirectory('gradle')
250+
.existsSync()) {
251+
gradleWrapperPropertiesDirectory =
252+
gradleWrapperPropertiesDirectory.childDirectory('app');
253+
}
254+
final File gradleWrapperPropertiesFile =
255+
gradleWrapperPropertiesDirectory
256+
.childDirectory('gradle')
257+
.childDirectory('wrapper')
258+
.childFile('gradle-wrapper.properties');
259+
260+
final String gradleWrapperPropertiesContents =
261+
gradleWrapperPropertiesFile.readAsStringSync();
262+
final RegExp validGradleDistributionUrl =
263+
RegExp(r'^\s*distributionUrl\s*=\s*.*\.zip', multiLine: true);
264+
if (!validGradleDistributionUrl
265+
.hasMatch(gradleWrapperPropertiesContents)) {
266+
return PackageResult.fail(<String>[
267+
'Unable to find a "distributionUrl" entry to update for ${package.displayName}.'
268+
]);
269+
}
270+
271+
print(
272+
'${indentation}Updating ${getRelativePosixPath(example.directory, from: package.directory)} to "$_targetVersion"');
273+
final String newGradleWrapperPropertiesContents =
274+
gradleWrapperPropertiesContents.replaceFirst(
275+
validGradleDistributionUrl,
276+
'distributionUrl=https\\://services.gradle.org/distributions/gradle-$_targetVersion-all.zip');
277+
// TODO(camsim99): Validate current AGP version against target Gradle
278+
// version: https://github.com/flutter/flutter/issues/133887.
279+
gradleWrapperPropertiesFile
280+
.writeAsStringSync(newGradleWrapperPropertiesContents);
281+
}
282+
return updateRanForExamples
283+
? PackageResult.success()
284+
: PackageResult.skip('No example apps run on Android.');
285+
}
286+
return PackageResult.fail(<String>[
287+
'Target Android dependency $_androidDependency is unrecognized.'
288+
]);
289+
}
290+
184291
/// Returns information about the current dependency of [package] on
185292
/// the package named [dependencyName], or null if there is no dependency.
186293
_PubDependencyInfo? _getPubDependencyInfo(

0 commit comments

Comments
 (0)