@@ -19,6 +19,7 @@ import 'common/repository_package.dart';
1919
2020const int _exitIncorrectTargetDependency = 3 ;
2121const 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