From 29a6c648ca4d2b813a6787644fdfe20421a2570a Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Fri, 13 Dec 2024 11:43:25 -0800 Subject: [PATCH] precompile generate_gradle_lockfile script BEFORE updating pub dependencies (#160059) Avoid situations like https://github.com/flutter/flutter/issues/160055 --- .../core/lib/src/packages_autoroller.dart | 63 +++++++++++++++++-- dev/conductor/core/lib/src/repository.dart | 11 +++- .../core/test/packages_autoroller_test.dart | 45 ++++++++++++- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/dev/conductor/core/lib/src/packages_autoroller.dart b/dev/conductor/core/lib/src/packages_autoroller.dart index ca3f67f01e..89f1c06847 100644 --- a/dev/conductor/core/lib/src/packages_autoroller.dart +++ b/dev/conductor/core/lib/src/packages_autoroller.dart @@ -85,6 +85,7 @@ This PR was generated by the automated final String orgName; Future roll() async { + final Directory tempDir = framework.fileSystem.systemTempDirectory.createTempSync(); try { await authLogin(); final bool openPrAlready = await hasOpenPrs(); @@ -92,18 +93,26 @@ This PR was generated by the automated // Don't open multiple roll PRs. return; } + final Directory frameworkDir = await framework.checkoutDirectory; + final String regenerateBinary = await _prebuildRegenerateGradleLockfilesBinary(frameworkDir, tempDir); final bool didUpdate = await updatePackages(); if (!didUpdate) { log('Packages are already at latest.'); return; } - await _regenerateGradleLockfiles(await framework.checkoutDirectory); + await _regenerateGradleLockfiles(frameworkDir, regenerateBinary); await pushBranch(); await createPr(repository: await framework.checkoutDirectory); await authLogout(); } on Exception catch (exception) { final String message = _redactToken(exception.toString()); throw Exception('${exception.runtimeType}: $message'); + } finally { + try { + tempDir.deleteSync(recursive: true); + } on FileSystemException { + // Ignore failures + } } } @@ -139,12 +148,58 @@ This PR was generated by the automated return true; } - Future _regenerateGradleLockfiles(Directory repoRoot) async { + Future _prebuildRegenerateGradleLockfilesBinary(Directory repoRoot, Directory tempDir) async { + final String entrypoint = '${repoRoot.path}/dev/tools/bin/generate_gradle_lockfiles.dart'; + final File target = tempDir.childFile('generate_gradle_lockfiles'); + await framework.streamDart( + ['pub', 'get'], + workingDirectory: '${repoRoot.path}/dev/tools', + ); await framework.streamDart([ - '${repoRoot.path}/dev/tools/bin/generate_gradle_lockfiles.dart', + 'compile', + 'exe', + entrypoint, + '-o', + target.path, + ]); + + assert( + target.existsSync(), + 'expected ${target.path} to exist after compilation, but it did not.', + ); + + processManager.runSync(['chmod', '+x', target.path]); + + return target.path; + } + + Future _regenerateGradleLockfiles(Directory repoRoot, String regenerateBinary) async { + final List cmd = [ + regenerateBinary, '--no-gradle-generation', '--no-exclusion', - ]); + ]; + final io.Process regenerateProcess = await processManager.start(cmd); + regenerateProcess + .stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((String line) => stdio.printTrace('[stdout] $line')); + regenerateProcess + .stderr + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((String line) => stdio.printTrace('[stderr] $line')); + + final int exitCode = await regenerateProcess.exitCode; + if (exitCode != 0) { + throw io.ProcessException( + cmd.first, + cmd.sublist(1), + 'Process failed', + exitCode, + ); + } switch (CheckoutStatePostGradleRegeneration(await framework.gitStatus(), framework.fileSystem.path)) { // If the git checkout is clean, we did not update any lockfiles and we do // not need an additional commit. diff --git a/dev/conductor/core/lib/src/repository.dart b/dev/conductor/core/lib/src/repository.dart index b775b468fa..b6155d5503 100644 --- a/dev/conductor/core/lib/src/repository.dart +++ b/dev/conductor/core/lib/src/repository.dart @@ -671,16 +671,23 @@ class FrameworkRepository extends Repository { cmd, workingDirectory: workingDirectory, ); - process + final StreamSubscription stdoutSub = process .stdout .transform(utf8.decoder) .transform(const LineSplitter()) .listen(stdoutCallback ?? stdio.printTrace); - process + final StreamSubscription stderrSub = process .stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen(stderrCallback ?? stdio.printError); + await Future.wait(>[ + stdoutSub.asFuture(), + stderrSub.asFuture(), + ]); + unawaited(stdoutSub.cancel()); + unawaited(stderrSub.cancel()); + final int exitCode = await process.exitCode; if (exitCode != 0) { throw io.ProcessException( diff --git a/dev/conductor/core/test/packages_autoroller_test.dart b/dev/conductor/core/test/packages_autoroller_test.dart index 0379137122..ddd3150e0f 100644 --- a/dev/conductor/core/test/packages_autoroller_test.dart +++ b/dev/conductor/core/test/packages_autoroller_test.dart @@ -281,6 +281,27 @@ void main() { 'rev-parse', 'HEAD', ], stdout: 'deadbeef'), + const FakeCommand( + command: [ + '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/bin/dart', + 'pub', + 'get', + ], + workingDirectory: '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/dev/tools', + ), + FakeCommand(command: const [ + '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/bin/dart', + 'compile', + 'exe', + '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/dev/tools/bin/generate_gradle_lockfiles.dart', + '-o', + '/.tmp_rand0/rand0/generate_gradle_lockfiles', + ], onRun: (_) => fileSystem.file('/.tmp_rand0/rand0/generate_gradle_lockfiles').createSync()), + const FakeCommand(command: [ + 'chmod', + '+x', + '/.tmp_rand0/rand0/generate_gradle_lockfiles', + ]), const FakeCommand(command: [ 'git', 'ls-remote', @@ -373,6 +394,27 @@ void main() { 'rev-parse', 'HEAD', ], stdout: 'deadbeef'), + const FakeCommand( + command: [ + '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/bin/dart', + 'pub', + 'get', + ], + workingDirectory: '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/dev/tools', + ), + FakeCommand(command: const [ + '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/bin/dart', + 'compile', + 'exe', + '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/dev/tools/bin/generate_gradle_lockfiles.dart', + '-o', + '/.tmp_rand0/rand0/generate_gradle_lockfiles', + ], onRun: (_) => fileSystem.file('/.tmp_rand0/rand0/generate_gradle_lockfiles').createSync()), + const FakeCommand(command: [ + 'chmod', + '+x', + '/.tmp_rand0/rand0/generate_gradle_lockfiles', + ]), const FakeCommand(command: [ 'git', 'ls-remote', @@ -427,8 +469,7 @@ void main() { 'HEAD', ], stdout: '000deadbeef'), const FakeCommand(command: [ - '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/bin/dart', - '$checkoutsParentDirectory/flutter_conductor_checkouts/framework/dev/tools/bin/generate_gradle_lockfiles.dart', + '/.tmp_rand0/rand0/generate_gradle_lockfiles', '--no-gradle-generation', '--no-exclusion', ]),