diff --git a/dev/conductor/core/lib/src/git.dart b/dev/conductor/core/lib/src/git.dart index d0a257f5a7..5d39e2781c 100644 --- a/dev/conductor/core/lib/src/git.dart +++ b/dev/conductor/core/lib/src/git.dart @@ -10,7 +10,7 @@ import './globals.dart'; /// A wrapper around git process calls that can be mocked for unit testing. class Git { - Git(this.processManager) : assert(processManager != null); + const Git(this.processManager); final ProcessManager processManager; diff --git a/dev/conductor/core/lib/src/start.dart b/dev/conductor/core/lib/src/start.dart index a1c672c84e..5a61183494 100644 --- a/dev/conductor/core/lib/src/start.dart +++ b/dev/conductor/core/lib/src/start.dart @@ -114,9 +114,6 @@ class StartCommand extends Command { final ProcessManager processManager; final Stdio stdio; - /// Git revision for the currently running Conductor. - late final String conductorVersion; - @override String get name => 'start'; @@ -125,14 +122,6 @@ class StartCommand extends Command { @override Future run() async { - final Git git = Git(processManager); - conductorVersion = (await git.getOutput( - ['rev-parse', 'HEAD'], - 'look up the current revision.', - workingDirectory: flutterRoot.path, - )).trim(); - - assert(conductorVersion.isNotEmpty); final ArgResults argumentResults = argResults!; if (!platform.isMacOS && !platform.isLinux) { throw ConductorException( @@ -140,14 +129,6 @@ class StartCommand extends Command { ); } - final File stateFile = checkouts.fileSystem.file( - getValueFromEnvOrArgs(kStateOption, argumentResults, platform.environment), - ); - if (stateFile.existsSync()) { - throw ConductorException( - 'Error! A persistent state file already found at ${argResults![kStateOption]}.\n\n' - 'Run `conductor clean` to cancel a previous release.'); - } final String frameworkUpstream = getValueFromEnvOrArgs( kFrameworkUpstreamOption, argumentResults, @@ -199,7 +180,96 @@ class StartCommand extends Command { argumentResults, platform.environment, )!; + final File stateFile = checkouts.fileSystem.file( + getValueFromEnvOrArgs(kStateOption, argumentResults, platform.environment), + ); + final StartContext context = StartContext( + candidateBranch: candidateBranch, + checkouts: checkouts, + dartRevision: dartRevision, + engineCherrypickRevisions: engineCherrypickRevisions, + engineMirror: engineMirror, + engineUpstream: engineUpstream, + flutterRoot: flutterRoot, + frameworkCherrypickRevisions: frameworkCherrypickRevisions, + frameworkMirror: frameworkMirror, + frameworkUpstream: frameworkUpstream, + incrementLetter: incrementLetter, + processManager: processManager, + releaseChannel: releaseChannel, + stateFile: stateFile, + stdio: stdio, + ); + return context.run(); + } +} + +/// Context for starting a new release. +/// +/// This is a frontend-agnostic implementation. +class StartContext { + StartContext({ + required this.candidateBranch, + required this.checkouts, + required this.dartRevision, + required this.engineCherrypickRevisions, + required this.engineMirror, + required this.engineUpstream, + required this.frameworkCherrypickRevisions, + required this.frameworkMirror, + required this.frameworkUpstream, + required this.flutterRoot, + required this.incrementLetter, + required this.processManager, + required this.releaseChannel, + required this.stateFile, + required this.stdio, + }) : git = Git(processManager); + + final String candidateBranch; + final Checkouts checkouts; + final String? dartRevision; + final List engineCherrypickRevisions; + final String engineMirror; + final String engineUpstream; + final List frameworkCherrypickRevisions; + final String frameworkMirror; + final String frameworkUpstream; + final Directory flutterRoot; + final String incrementLetter; + final Git git; + final ProcessManager processManager; + final String releaseChannel; + final File stateFile; + final Stdio stdio; + + /// Git revision for the currently running Conductor. + Future get conductorVersion async { + if (_conductorVersion != null) { + return Future.value(_conductorVersion); + } + _conductorVersion = (await git.getOutput( + ['rev-parse', 'HEAD'], + 'look up the current revision.', + workingDirectory: flutterRoot.path, + )).trim(); + if (_conductorVersion == null || _conductorVersion!.isEmpty) { + throw ConductorException( + 'Failed to determine the git revision of the Flutter SDK\n' + 'Working directory: ${flutterRoot.path}' + ); + } + return _conductorVersion!; + } + String? _conductorVersion; + + Future run() async { + if (stateFile.existsSync()) { + throw ConductorException( + 'Error! A persistent state file already found at ${stateFile.path}.\n\n' + 'Run `conductor clean` to cancel a previous release.'); + } if (!releaseCandidateBranchRegex.hasMatch(candidateBranch)) { throw ConductorException( 'Invalid release candidate branch "$candidateBranch". Text should ' @@ -233,8 +303,8 @@ class StartCommand extends Command { final String workingBranchName = 'cherrypicks-$candidateBranch'; await engine.newBranch(workingBranchName); - if (dartRevision != null && dartRevision.isNotEmpty) { - await engine.updateDartRevision(dartRevision); + if (dartRevision != null && dartRevision!.isNotEmpty) { + await engine.updateDartRevision(dartRevision!); await engine.commit('Update Dart SDK to $dartRevision', addFirst: true); } final List engineCherrypicks = (await _sortCherrypicks( @@ -349,7 +419,7 @@ class StartCommand extends Command { state.currentPhase = ReleasePhase.APPLY_ENGINE_CHERRYPICKS; - state.conductorVersion = conductorVersion; + state.conductorVersion = await conductorVersion; stdio.printTrace('Writing state to file ${stateFile.path}...'); diff --git a/dev/conductor/core/test/start_test.dart b/dev/conductor/core/test/start_test.dart index 73a6d3f020..a73982567e 100644 --- a/dev/conductor/core/test/start_test.dart +++ b/dev/conductor/core/test/start_test.dart @@ -82,7 +82,21 @@ void main() { operatingSystem: 'windows', ); await expectLater( - () async => runner.run(['start']), + () async => runner.run([ + 'start', + '--$kFrameworkMirrorOption', + frameworkMirror, + '--$kEngineMirrorOption', + engineMirror, + '--$kCandidateOption', + candidateBranch, + '--$kReleaseOption', + 'dev', + '--$kStateOption', + '/path/to/statefile.json', + '--$kIncrementOption', + 'y', + ]), throwsExceptionWith( 'Error! This tool is only supported on macOS and Linux', ), @@ -236,12 +250,12 @@ void main() { final CommandRunner runner = createRunner( commands: [ + ...engineCommands, + ...frameworkCommands, const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], stdout: revision, ), - ...engineCommands, - ...frameworkCommands, ], ); @@ -420,12 +434,12 @@ void main() { final CommandRunner runner = createRunner( commands: [ + ...engineCommands, + ...frameworkCommands, const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], stdout: revision, ), - ...engineCommands, - ...frameworkCommands, ], );