Skip to content

Commit

Permalink
[tool] Add Android dependency (gradle) option to update dependencies …
Browse files Browse the repository at this point in the history
…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
```
  • Loading branch information
camsim99 authored Sep 8, 2023
1 parent c6fe5fa commit aaae5ef
Show file tree
Hide file tree
Showing 2 changed files with 353 additions and 3 deletions.
113 changes: 110 additions & 3 deletions script/tool/lib/src/update_dependency_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'common/repository_package.dart';

const int _exitIncorrectTargetDependency = 3;
const int _exitNoTargetVersion = 4;
const int _exitInvalidTargetVersion = 5;

/// A command to update a dependency in packages.
///
Expand All @@ -38,23 +39,34 @@ class UpdateDependencyCommand extends PackageLoopingCommand {
_pubPackageFlag,
help: 'A pub package to update.',
);
argParser.addOption(_androidDependency,
help: 'An Android dependency to update.',
allowed: <String>[
'gradle',
],
allowedHelp: <String, String>{
'gradle': 'Updates Gradle version used in plugin example apps.',
});
argParser.addOption(
_versionFlag,
help: 'The version to update to.\n\n'
'- For pub, defaults to the latest published version if not '
'provided. This can be any constraint that pubspec.yaml allows; a '
'specific version will be treated as the exact version for '
'dependencies that are alread pinned, or a ^ range for those that '
'are unpinned.',
'are unpinned.\n'
'- For Android dependencies, a version must be provided.',
);
}

static const String _pubPackageFlag = 'pub-package';
static const String _androidDependency = 'android-dependency';
static const String _versionFlag = 'version';

final PubVersionFinder _pubVersionFinder;

late final String? _targetPubPackage;
late final String? _targetAndroidDependency;
late final String _targetVersion;

@override
Expand All @@ -72,14 +84,19 @@ class UpdateDependencyCommand extends PackageLoopingCommand {

@override
Future<void> initializeRun() async {
const Set<String> targetFlags = <String>{_pubPackageFlag};
const Set<String> targetFlags = <String>{
_pubPackageFlag,
_androidDependency
};
final Set<String> passedTargetFlags =
targetFlags.where((String flag) => argResults![flag] != null).toSet();
if (passedTargetFlags.length != 1) {
printError(
'Exactly one of the target flags must be provided: (${targetFlags.join(', ')})');
throw ToolExit(_exitIncorrectTargetDependency);
}

// Setup for updating pub dependency.
_targetPubPackage = getNullableStringArg(_pubPackageFlag);
if (_targetPubPackage != null) {
final String? version = getNullableStringArg(_versionFlag);
Expand All @@ -102,6 +119,33 @@ ${response.httpResponse.body}
}
} else {
_targetVersion = version;
return;
}
}

// Setup for updating Android dependency.
_targetAndroidDependency = getNullableStringArg(_androidDependency);
if (_targetAndroidDependency != null) {
final String? version = getNullableStringArg(_versionFlag);
if (version == null) {
printError('A version must be provided to update this dependency.');
throw ToolExit(_exitNoTargetVersion);
} else if (_targetAndroidDependency == 'gradle') {
final RegExp validGradleVersionPattern = RegExp(r'^\d+(?:\.\d+){1,2}$');
final bool isValidGradleVersion =
validGradleVersionPattern.stringMatch(version) == version;
if (!isValidGradleVersion) {
printError(
'A version with a valid format (maximum 2-3 numbers separated by period) must be provided.');
throw ToolExit(_exitInvalidTargetVersion);
}
_targetVersion = version;
return;
} else {
// TODO(camsim99): Add other supported Android dependencies like the Android SDK and AGP.
printError(
'Target Android dependency $_targetAndroidDependency is unrecognized.');
throw ToolExit(_exitIncorrectTargetDependency);
}
}
}
Expand All @@ -116,7 +160,11 @@ ${response.httpResponse.body}
if (_targetPubPackage != null) {
return _runForPubDependency(package, _targetPubPackage!);
}
// TODO(stuartmorgan): Add othe dependency types here (e.g., maven).
if (_targetAndroidDependency != null) {
return _runForAndroidDependency(package);
}

// TODO(stuartmorgan): Add other dependency types here (e.g., maven).

return PackageResult.fail();
}
Expand Down Expand Up @@ -181,6 +229,65 @@ ${response.httpResponse.body}
return PackageResult.success();
}

/// Handles all of the updates for [package] when the target dependency is
/// an Android dependency.
Future<PackageResult> _runForAndroidDependency(
RepositoryPackage package) async {
if (_targetAndroidDependency == 'gradle') {
final Iterable<RepositoryPackage> packageExamples = package.getExamples();
bool updateRanForExamples = false;
for (final RepositoryPackage example in packageExamples) {
if (!example.platformDirectory(FlutterPlatform.android).existsSync()) {
continue;
}

updateRanForExamples = true;
Directory gradleWrapperPropertiesDirectory =
example.platformDirectory(FlutterPlatform.android);
if (gradleWrapperPropertiesDirectory
.childDirectory('app')
.childDirectory('gradle')
.existsSync()) {
gradleWrapperPropertiesDirectory =
gradleWrapperPropertiesDirectory.childDirectory('app');
}
final File gradleWrapperPropertiesFile =
gradleWrapperPropertiesDirectory
.childDirectory('gradle')
.childDirectory('wrapper')
.childFile('gradle-wrapper.properties');

final String gradleWrapperPropertiesContents =
gradleWrapperPropertiesFile.readAsStringSync();
final RegExp validGradleDistributionUrl =
RegExp(r'^\s*distributionUrl\s*=\s*.*\.zip', multiLine: true);
if (!validGradleDistributionUrl
.hasMatch(gradleWrapperPropertiesContents)) {
return PackageResult.fail(<String>[
'Unable to find a "distributionUrl" entry to update for ${package.displayName}.'
]);
}

print(
'${indentation}Updating ${getRelativePosixPath(example.directory, from: package.directory)} to "$_targetVersion"');
final String newGradleWrapperPropertiesContents =
gradleWrapperPropertiesContents.replaceFirst(
validGradleDistributionUrl,
'distributionUrl=https\\://services.gradle.org/distributions/gradle-$_targetVersion-all.zip');
// TODO(camsim99): Validate current AGP version against target Gradle
// version: https://github.com/flutter/flutter/issues/133887.
gradleWrapperPropertiesFile
.writeAsStringSync(newGradleWrapperPropertiesContents);
}
return updateRanForExamples
? PackageResult.success()
: PackageResult.skip('No example apps run on Android.');
}
return PackageResult.fail(<String>[
'Target Android dependency $_androidDependency is unrecognized.'
]);
}

/// Returns information about the current dependency of [package] on
/// the package named [dependencyName], or null if there is no dependency.
_PubDependencyInfo? _getPubDependencyInfo(
Expand Down
Loading

0 comments on commit aaae5ef

Please sign in to comment.