From 0b9d18f8faf8fdce8d529aaeecb386bade318bf4 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Mon, 21 Nov 2022 12:04:52 -0800 Subject: [PATCH] [flutter_tools] Add flutter update-packages --synthetic-package-path (#115665) * allow persisting synthetic package with option * allow presisting synthetic packages dir * fix * fix bug and add tests * clean up tests * nits --- .../lib/src/commands/update_packages.dart | 127 +++++++++++------- .../hermetic/update_packages_test.dart | 49 +++++-- .../general.shard/update_packages_test.dart | 1 + 3 files changed, 114 insertions(+), 63 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index 4cfd2f2d43..ffaf226457 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -99,6 +99,14 @@ class UpdatePackagesCommand extends FlutterCommand { abbr: 'j', help: 'Causes the "pub get" runs to happen concurrently on this many ' 'CPUs. Defaults to the number of CPUs that this machine has.', + ) + ..addOption( + 'synthetic-package-path', + help: 'Write the synthetic monolithic pub package generated to do ' + 'version solving to a persistent path. By default, a temporary ' + 'directory that is deleted before the command exits. By ' + 'providing this path, a Flutter maintainer can inspect further ' + 'exactly how version solving was achieved.', ); } @@ -106,7 +114,10 @@ class UpdatePackagesCommand extends FlutterCommand { final String name = 'update-packages'; @override - final String description = 'Update the packages inside the Flutter repo.'; + final String description = 'Update the packages inside the Flutter repo. ' + 'This is intended for CI and repo maintainers. ' + 'Normal Flutter developers should not have to ' + 'use this command.'; @override final List aliases = ['upgrade-packages']; @@ -144,6 +155,22 @@ class UpdatePackagesCommand extends FlutterCommand { ..writeAsBytesSync(data, flush: true); } + late final Directory _syntheticPackageDir = (() { + final String? optionPath = stringArg('synthetic-package-path'); + if (optionPath == null) { + return globals.fs.systemTempDirectory.createTempSync('flutter_update_packages.'); + } + final Directory syntheticPackageDir = globals.fs.directory(optionPath); + if (!syntheticPackageDir.existsSync()) { + syntheticPackageDir.createSync(recursive: true); + } + globals.printStatus( + 'The synthetic package with all pub dependencies across the repo will ' + 'be written to ${syntheticPackageDir.absolute.path}.', + ); + return syntheticPackageDir; + })(); + @override Future runCommand() async { final List packages = runner!.getRepoPackages(); @@ -218,15 +245,19 @@ class UpdatePackagesCommand extends FlutterCommand { // attempt to download any necessary package versions to the pub cache to // warm the cache. final PubDependencyTree tree = PubDependencyTree(); // object to collect results - final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_update_packages.'); await _generateFakePackage( - tempDir: tempDir, + tempDir: _syntheticPackageDir, dependencies: doUpgrade ? explicitDependencies.values : allDependencies.values, pubspecs: pubspecs, tree: tree, doUpgrade: doUpgrade, ); + // Only delete the synthetic package if it was done in a temp directory + if (stringArg('synthetic-package-path') == null) { + _syntheticPackageDir.deleteSync(recursive: true); + } + if (doUpgrade) { final bool done = _upgradePubspecs( tree: tree, @@ -380,57 +411,49 @@ class UpdatePackagesCommand extends FlutterCommand { required bool doUpgrade, }) async { Directory? temporaryFlutterSdk; - try { - final File fakePackage = _pubspecFor(tempDir); - fakePackage.createSync(); - fakePackage.writeAsStringSync( - generateFakePubspec( - dependencies, - doUpgrade: doUpgrade, - ), + final Directory syntheticPackageDir = tempDir.childDirectory('synthetic_package'); + final File fakePackage = _pubspecFor(syntheticPackageDir); + fakePackage.createSync(recursive: true); + fakePackage.writeAsStringSync( + generateFakePubspec( + dependencies, + doUpgrade: doUpgrade, + ), + ); + // Create a synthetic flutter SDK so that transitive flutter SDK + // constraints are not affected by this upgrade. + if (doUpgrade) { + temporaryFlutterSdk = createTemporaryFlutterSdk( + globals.logger, + globals.fs, + globals.fs.directory(Cache.flutterRoot), + pubspecs, + tempDir, ); - // Create a synthetic flutter SDK so that transitive flutter SDK - // constraints are not affected by this upgrade. - if (doUpgrade) { - temporaryFlutterSdk = createTemporaryFlutterSdk( - globals.logger, - globals.fs, - globals.fs.directory(Cache.flutterRoot), - pubspecs, - ); - } + } - // Next we run "pub get" on it in order to force the download of any - // needed packages to the pub cache, upgrading if requested. - await pub.get( + // Next we run "pub get" on it in order to force the download of any + // needed packages to the pub cache, upgrading if requested. + await pub.get( + context: PubContext.updatePackages, + project: FlutterProject.fromDirectory(syntheticPackageDir), + upgrade: doUpgrade, + offline: boolArgDeprecated('offline'), + flutterRootOverride: temporaryFlutterSdk?.path, + ); + + if (doUpgrade) { + // If upgrading, we run "pub deps --style=compact" on the result. We + // pipe all the output to tree.fill(), which parses it so that it can + // create a graph of all the dependencies so that we can figure out the + // transitive dependencies later. It also remembers which version was + // selected for each package. + await pub.batch( + ['deps', '--style=compact'], context: PubContext.updatePackages, - project: FlutterProject.fromDirectory(tempDir), - upgrade: doUpgrade, - offline: boolArgDeprecated('offline'), - flutterRootOverride: temporaryFlutterSdk?.path, + directory: syntheticPackageDir.path, + filter: tree.fill, ); - - if (doUpgrade) { - // If upgrading, we run "pub deps --style=compact" on the result. We - // pipe all the output to tree.fill(), which parses it so that it can - // create a graph of all the dependencies so that we can figure out the - // transitive dependencies later. It also remembers which version was - // selected for each package. - await pub.batch( - ['deps', '--style=compact'], - context: PubContext.updatePackages, - directory: tempDir.path, - filter: tree.fill, - ); - } - } finally { - // Cleanup the temporary SDK - try { - temporaryFlutterSdk?.deleteSync(recursive: true); - } on FileSystemException { - // Failed to delete temporary SDK. - } - tempDir.deleteSync(recursive: true); } } @@ -1577,6 +1600,7 @@ Directory createTemporaryFlutterSdk( FileSystem fileSystem, Directory realFlutter, List pubspecs, + Directory tempDir, ) { final Set currentPackages = {}; for (final FileSystemEntity entity in realFlutter.childDirectory('packages').listSync()) { @@ -1591,8 +1615,7 @@ Directory createTemporaryFlutterSdk( pubspecsByName[pubspec.name] = pubspec; } - final Directory directory = fileSystem.systemTempDirectory - .createTempSync('flutter_upgrade_sdk.') + final Directory directory = tempDir.childDirectory('flutter_upgrade_sdk') ..createSync(); // Fill in version info. realFlutter.childFile('version') diff --git a/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart index 92d4f597c5..cc2e71490b 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart @@ -87,6 +87,7 @@ void main() { late Directory flutterSdk; late Directory flutter; late FakePub pub; + late FakeProcessManager processManager; setUpAll(() { Cache.disableLocking(); @@ -105,13 +106,14 @@ void main() { flutter.childFile('pubspec.yaml').writeAsStringSync(kFlutterPubspecYaml); Cache.flutterRoot = flutterSdk.absolute.path; pub = FakePub(fileSystem); + processManager = FakeProcessManager.empty(); }); testUsingContext('updates packages', () async { final UpdatePackagesCommand command = UpdatePackagesCommand(); await createTestCommandRunner(command).run(['update-packages']); expect(pub.pubGetDirectories, equals([ - '/.tmp_rand0/flutter_update_packages.rand0', + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', '/flutter/examples', '/flutter/packages/flutter', ])); @@ -119,9 +121,9 @@ void main() { }, overrides: { Pub: () => pub, FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), + ProcessManager: () => processManager, Cache: () => Cache.test( - processManager: FakeProcessManager.any(), + processManager: processManager, ), }); @@ -132,19 +134,19 @@ void main() { '--force-upgrade', ]); expect(pub.pubGetDirectories, equals([ - '/.tmp_rand0/flutter_update_packages.rand0', + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', '/flutter/examples', '/flutter/packages/flutter', ])); expect(pub.pubBatchDirectories, equals([ - '/.tmp_rand0/flutter_update_packages.rand0', + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', ])); }, overrides: { Pub: () => pub, FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), + ProcessManager: () => processManager, Cache: () => Cache.test( - processManager: FakeProcessManager.any(), + processManager: processManager, ), }); @@ -156,19 +158,44 @@ void main() { '--jobs=1', ]); expect(pub.pubGetDirectories, equals([ - '/.tmp_rand0/flutter_update_packages.rand0', + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', '/flutter/examples', '/flutter/packages/flutter', ])); expect(pub.pubBatchDirectories, equals([ - '/.tmp_rand0/flutter_update_packages.rand0', + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', ])); }, overrides: { Pub: () => pub, FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), + ProcessManager: () => processManager, Cache: () => Cache.test( - processManager: FakeProcessManager.any(), + processManager: processManager, + ), + }); + + testUsingContext('force updates packages --synthetic-package-path', () async { + final UpdatePackagesCommand command = UpdatePackagesCommand(); + const String dir = '/path/to/synthetic/package'; + await createTestCommandRunner(command).run([ + 'update-packages', + '--force-upgrade', + '--synthetic-package-path=$dir', + ]); + expect(pub.pubGetDirectories, equals([ + '$dir/synthetic_package', + '/flutter/examples', + '/flutter/packages/flutter', + ])); + expect(pub.pubBatchDirectories, equals([ + '$dir/synthetic_package', + ])); + }, overrides: { + Pub: () => pub, + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Cache: () => Cache.test( + processManager: processManager, ), }); }); diff --git a/packages/flutter_tools/test/general.shard/update_packages_test.dart b/packages/flutter_tools/test/general.shard/update_packages_test.dart index a4abc351f4..32f7445543 100644 --- a/packages/flutter_tools/test/general.shard/update_packages_test.dart +++ b/packages/flutter_tools/test/general.shard/update_packages_test.dart @@ -121,6 +121,7 @@ void main() { fileSystem, flutterSdk, [flutterPubspec], + fileSystem.systemTempDirectory, ); expect(result, exists);