mirror of
https://github.com/flutter/packages.git
synced 2025-06-29 14:18:54 +08:00
[Tool] New tool to download android dependencies (#4408)
This pr is pushed for high level feedback/conversation. I will add tests before serious review. should be read in conjuction with https://flutter-review.googlesource.com/c/recipes/+/46980 - Create new top level command to run flutter dependencies on changed packages - when running android tests download dependencies before running tests https://github.com/flutter/flutter/issues/120119
This commit is contained in:
@ -1,6 +1,11 @@
|
||||
tasks:
|
||||
- name: prepare tool
|
||||
script: .ci/scripts/prepare_tool.sh
|
||||
infra_step: true # Note infra steps failing prevents "always" from running.
|
||||
- name: download android deps
|
||||
script: script/tool_runner.sh
|
||||
infra_step: true
|
||||
args: ["fetch-deps"]
|
||||
- name: build examples
|
||||
script: script/tool_runner.sh
|
||||
args: ["build-examples", "--apk"]
|
||||
|
74
script/tool/lib/src/fetch_deps_command.dart
Normal file
74
script/tool/lib/src/fetch_deps_command.dart
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
||||
import 'common/core.dart';
|
||||
import 'common/gradle.dart';
|
||||
import 'common/output_utils.dart';
|
||||
import 'common/package_looping_command.dart';
|
||||
import 'common/plugin_utils.dart';
|
||||
import 'common/repository_package.dart';
|
||||
|
||||
/// Download dependencies for the following platforms {android}.
|
||||
///
|
||||
/// Specficially each platform runs:
|
||||
/// Android: 'gradlew dependencies'.
|
||||
/// Dart: TBD (flutter/flutter/issues/130279)
|
||||
/// iOS: TBD (flutter/flutter/issues/130280)
|
||||
///
|
||||
/// See https://docs.gradle.org/6.4/userguide/core_dependency_management.html#sec:dependency-mgmt-in-gradle.
|
||||
class FetchDepsCommand extends PackageLoopingCommand {
|
||||
/// Creates an instance of the fetch-deps command.
|
||||
FetchDepsCommand(
|
||||
super.packagesDir, {
|
||||
super.processRunner,
|
||||
super.platform,
|
||||
});
|
||||
|
||||
@override
|
||||
final String name = 'fetch-deps';
|
||||
|
||||
@override
|
||||
final String description = 'Fetches dependencies for plugins.\n'
|
||||
'Runs "gradlew dependencies" on Android plugins.\n'
|
||||
'Dart see flutter/flutter/issues/130279\n'
|
||||
'iOS plugins see flutter/flutter/issues/130280\n'
|
||||
'\n'
|
||||
'Requires the examples to have been built at least once before running.';
|
||||
|
||||
@override
|
||||
Future<PackageResult> runForPackage(RepositoryPackage package) async {
|
||||
if (!pluginSupportsPlatform(platformAndroid, package,
|
||||
requiredMode: PlatformSupport.inline)) {
|
||||
return PackageResult.skip(
|
||||
'Plugin does not have an Android implementation.');
|
||||
}
|
||||
|
||||
for (final RepositoryPackage example in package.getExamples()) {
|
||||
final GradleProject gradleProject = GradleProject(example,
|
||||
processRunner: processRunner, platform: platform);
|
||||
|
||||
if (!gradleProject.isConfigured()) {
|
||||
final int exitCode = await processRunner.runAndStream(
|
||||
flutterCommand,
|
||||
<String>['build', 'apk', '--config-only'],
|
||||
workingDir: example.directory,
|
||||
);
|
||||
if (exitCode != 0) {
|
||||
printError('Unable to configure Gradle project.');
|
||||
return PackageResult.fail(<String>['Unable to configure Gradle.']);
|
||||
}
|
||||
}
|
||||
|
||||
final String packageName = package.directory.basename;
|
||||
|
||||
final int exitCode = await gradleProject.runCommand('$packageName:dependencies');
|
||||
if (exitCode != 0) {
|
||||
return PackageResult.fail();
|
||||
}
|
||||
}
|
||||
|
||||
return PackageResult.success();
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import 'dart_test_command.dart';
|
||||
import 'dependabot_check_command.dart';
|
||||
import 'drive_examples_command.dart';
|
||||
import 'federation_safety_check_command.dart';
|
||||
import 'fetch_deps_command.dart';
|
||||
import 'firebase_test_lab_command.dart';
|
||||
import 'fix_command.dart';
|
||||
import 'format_command.dart';
|
||||
@ -65,6 +66,7 @@ void main(List<String> args) {
|
||||
..addCommand(DependabotCheckCommand(packagesDir))
|
||||
..addCommand(DriveExamplesCommand(packagesDir))
|
||||
..addCommand(FederationSafetyCheckCommand(packagesDir))
|
||||
..addCommand(FetchDepsCommand(packagesDir))
|
||||
..addCommand(FirebaseTestLabCommand(packagesDir))
|
||||
..addCommand(FixCommand(packagesDir))
|
||||
..addCommand(FormatCommand(packagesDir))
|
||||
|
252
script/tool/test/fetch_deps_command_test.dart
Normal file
252
script/tool/test/fetch_deps_command_test.dart
Normal file
@ -0,0 +1,252 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_plugin_tools/src/common/core.dart';
|
||||
import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
|
||||
import 'package:flutter_plugin_tools/src/fetch_deps_command.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
import 'util.dart';
|
||||
|
||||
void main() {
|
||||
group('FetchDepsCommand', () {
|
||||
FileSystem fileSystem;
|
||||
late Directory packagesDir;
|
||||
late CommandRunner<void> runner;
|
||||
late MockPlatform mockPlatform;
|
||||
late RecordingProcessRunner processRunner;
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem();
|
||||
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
|
||||
mockPlatform = MockPlatform();
|
||||
processRunner = RecordingProcessRunner();
|
||||
final FetchDepsCommand command = FetchDepsCommand(
|
||||
packagesDir,
|
||||
processRunner: processRunner,
|
||||
platform: mockPlatform,
|
||||
);
|
||||
|
||||
runner =
|
||||
CommandRunner<void>('fetch_deps_test', 'Test for $FetchDepsCommand');
|
||||
runner.addCommand(command);
|
||||
});
|
||||
group('android', () {
|
||||
test('runs gradlew dependencies', () async {
|
||||
final RepositoryPackage plugin =
|
||||
createFakePlugin('plugin1', packagesDir, extraFiles: <String>[
|
||||
'example/android/gradlew',
|
||||
], platformSupport: <String, PlatformDetails>{
|
||||
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
||||
});
|
||||
|
||||
final Directory androidDir = plugin
|
||||
.getExamples()
|
||||
.first
|
||||
.platformDirectory(FlutterPlatform.android);
|
||||
|
||||
final List<String> output =
|
||||
await runCapturingPrint(runner, <String>['fetch-deps']);
|
||||
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
ProcessCall(
|
||||
androidDir.childFile('gradlew').path,
|
||||
const <String>['plugin1:dependencies'],
|
||||
androidDir.path,
|
||||
),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Running for plugin1'),
|
||||
contains('No issues found!'),
|
||||
]));
|
||||
});
|
||||
|
||||
test('runs on all examples', () async {
|
||||
final List<String> examples = <String>['example1', 'example2'];
|
||||
final RepositoryPackage plugin = createFakePlugin(
|
||||
'plugin1', packagesDir,
|
||||
examples: examples,
|
||||
extraFiles: <String>[
|
||||
'example/example1/android/gradlew',
|
||||
'example/example2/android/gradlew',
|
||||
],
|
||||
platformSupport: <String, PlatformDetails>{
|
||||
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
||||
});
|
||||
|
||||
final Iterable<Directory> exampleAndroidDirs = plugin.getExamples().map(
|
||||
(RepositoryPackage example) =>
|
||||
example.platformDirectory(FlutterPlatform.android));
|
||||
|
||||
final List<String> output =
|
||||
await runCapturingPrint(runner, <String>['fetch-deps']);
|
||||
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
for (final Directory directory in exampleAndroidDirs)
|
||||
ProcessCall(
|
||||
directory.childFile('gradlew').path,
|
||||
const <String>['plugin1:dependencies'],
|
||||
directory.path,
|
||||
),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Running for plugin1'),
|
||||
contains('No issues found!'),
|
||||
]));
|
||||
});
|
||||
|
||||
test('runs --config-only build if gradlew is missing', () async {
|
||||
final RepositoryPackage plugin = createFakePlugin(
|
||||
'plugin1', packagesDir, platformSupport: <String, PlatformDetails>{
|
||||
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
||||
});
|
||||
|
||||
final Directory androidDir = plugin
|
||||
.getExamples()
|
||||
.first
|
||||
.platformDirectory(FlutterPlatform.android);
|
||||
|
||||
final List<String> output =
|
||||
await runCapturingPrint(runner, <String>['fetch-deps']);
|
||||
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
ProcessCall(
|
||||
getFlutterCommand(mockPlatform),
|
||||
const <String>['build', 'apk', '--config-only'],
|
||||
plugin.getExamples().first.directory.path,
|
||||
),
|
||||
ProcessCall(
|
||||
androidDir.childFile('gradlew').path,
|
||||
const <String>['plugin1:dependencies'],
|
||||
androidDir.path,
|
||||
),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Running for plugin1'),
|
||||
contains('No issues found!'),
|
||||
]));
|
||||
});
|
||||
|
||||
test('fails if gradlew generation fails', () async {
|
||||
createFakePlugin('plugin1', packagesDir,
|
||||
platformSupport: <String, PlatformDetails>{
|
||||
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
||||
});
|
||||
|
||||
processRunner
|
||||
.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
|
||||
<FakeProcessInfo>[
|
||||
FakeProcessInfo(MockProcess(exitCode: 1)),
|
||||
];
|
||||
|
||||
Error? commandError;
|
||||
final List<String> output = await runCapturingPrint(
|
||||
runner, <String>['fetch-deps'], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(
|
||||
<Matcher>[
|
||||
contains('Unable to configure Gradle project'),
|
||||
],
|
||||
));
|
||||
});
|
||||
|
||||
test('fails if dependency download finds issues', () async {
|
||||
final RepositoryPackage plugin =
|
||||
createFakePlugin('plugin1', packagesDir, extraFiles: <String>[
|
||||
'example/android/gradlew',
|
||||
], platformSupport: <String, PlatformDetails>{
|
||||
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
||||
});
|
||||
|
||||
final String gradlewPath = plugin
|
||||
.getExamples()
|
||||
.first
|
||||
.platformDirectory(FlutterPlatform.android)
|
||||
.childFile('gradlew')
|
||||
.path;
|
||||
processRunner.mockProcessesForExecutable[gradlewPath] =
|
||||
<FakeProcessInfo>[
|
||||
FakeProcessInfo(MockProcess(exitCode: 1)),
|
||||
];
|
||||
|
||||
Error? commandError;
|
||||
final List<String> output = await runCapturingPrint(
|
||||
runner, <String>['fetch-deps'], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(
|
||||
<Matcher>[
|
||||
contains('The following packages had errors:'),
|
||||
],
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
test('skips non-Android plugins', () async {
|
||||
createFakePlugin('plugin1', packagesDir);
|
||||
|
||||
final List<String> output =
|
||||
await runCapturingPrint(runner, <String>['fetch-deps']);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(
|
||||
<Matcher>[
|
||||
contains(
|
||||
'SKIPPING: Plugin does not have an Android implementation.')
|
||||
],
|
||||
));
|
||||
});
|
||||
|
||||
test('skips non-inline plugins', () async {
|
||||
createFakePlugin('plugin1', packagesDir,
|
||||
platformSupport: <String, PlatformDetails>{
|
||||
platformAndroid: const PlatformDetails(PlatformSupport.federated)
|
||||
});
|
||||
|
||||
final List<String> output =
|
||||
await runCapturingPrint(runner, <String>['fetch-deps']);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(
|
||||
<Matcher>[
|
||||
contains(
|
||||
'SKIPPING: Plugin does not have an Android implementation.')
|
||||
],
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user