diff --git a/dev/conductor/core/lib/src/next.dart b/dev/conductor/core/lib/src/next.dart index 1b1937af33..d6bd21114b 100644 --- a/dev/conductor/core/lib/src/next.dart +++ b/dev/conductor/core/lib/src/next.dart @@ -48,12 +48,20 @@ class NextCommand extends Command { @override Future run() async { + final File stateFile = checkouts.fileSystem.file(argResults![kStateOption]); + if (!stateFile.existsSync()) { + throw ConductorException( + 'No persistent state file found at ${stateFile.path}.', + ); + } + final pb.ConductorState state = state_import.readStateFromFile(stateFile); + await NextContext( autoAccept: argResults![kYesFlag] as bool, checkouts: checkouts, force: argResults![kForceFlag] as bool, - stateFile: checkouts.fileSystem.file(argResults![kStateOption]), - ).run(); + stateFile: stateFile, + ).run(state); } } @@ -74,20 +82,12 @@ class NextContext { final Checkouts checkouts; final File stateFile; - Future run() async { + Future run(pb.ConductorState state) async { final Stdio stdio = checkouts.stdio; const List finishedStates = [ CherrypickState.COMPLETED, CherrypickState.ABANDONED, ]; - if (!stateFile.existsSync()) { - throw ConductorException( - 'No persistent state file found at ${stateFile.path}.', - ); - } - - final pb.ConductorState state = readStateFromFile(stateFile); - switch (state.currentPhase) { case pb.ReleasePhase.APPLY_ENGINE_CHERRYPICKS: final Remote upstream = Remote( @@ -149,7 +149,7 @@ class NextContext { ); if (!response) { stdio.printError('Aborting command.'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); return; } } @@ -175,7 +175,7 @@ class NextContext { ); if (!response) { stdio.printError('Aborting command.'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); return; } } @@ -284,7 +284,7 @@ class NextContext { ); if (!response) { stdio.printError('Aborting command.'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); return; } } @@ -319,7 +319,7 @@ class NextContext { ); if (!response) { stdio.printError('Aborting command.'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); return; } } @@ -354,7 +354,7 @@ class NextContext { ); if (!response) { stdio.printError('Aborting command.'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); return; } } @@ -377,7 +377,7 @@ class NextContext { ); if (!response) { stdio.printError('Aborting command.'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); return; } } @@ -390,13 +390,17 @@ class NextContext { state.currentPhase = nextPhase; stdio.printStatus(state_import.phaseInstructions(state)); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); } - /// Persist the state to a file. + /// Save the release's [state]. + /// + /// This can be overridden by frontends that may not persist the state to + /// disk, and/or may need to call additional update hooks each time the state + /// is updated. @visibleForOverriding - void writeStateToFile(File file, pb.ConductorState state, [List logs = const []]) { - state_import.writeStateToFile(file, state, logs); + void updateState(pb.ConductorState state, [List logs = const []]) { + state_import.writeStateToFile(stateFile, state, logs); } @visibleForTesting @@ -414,8 +418,4 @@ class NextContext { 'Unknown user input (expected "y" or "n"): $response', ); } - - /// Read the state from a file. - @visibleForOverriding - pb.ConductorState readStateFromFile(File file) => state_import.readStateFromFile(file); } diff --git a/dev/conductor/core/lib/src/start.dart b/dev/conductor/core/lib/src/start.dart index d45c6c0def..91134567d0 100644 --- a/dev/conductor/core/lib/src/start.dart +++ b/dev/conductor/core/lib/src/start.dart @@ -6,6 +6,7 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:fixnum/fixnum.dart'; +import 'package:meta/meta.dart' show visibleForOverriding; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -14,7 +15,7 @@ import './globals.dart'; import './proto/conductor_state.pb.dart' as pb; import './proto/conductor_state.pbenum.dart' show ReleasePhase; import './repository.dart'; -import './state.dart'; +import './state.dart' as state_import; import './stdio.dart'; import './version.dart'; @@ -39,7 +40,7 @@ class StartCommand extends Command { processManager = checkouts.processManager, fileSystem = checkouts.fileSystem, stdio = checkouts.stdio { - final String defaultPath = defaultStateFilePath(platform); + final String defaultPath = state_import.defaultStateFilePath(platform); argParser.addOption( kCandidateOption, help: 'The candidate branch the release will be based on.', @@ -401,9 +402,19 @@ class StartContext { stdio.printTrace('Writing state to file ${stateFile.path}...'); - writeStateToFile(stateFile, state, stdio.logs); + updateState(state, stdio.logs); - stdio.printStatus(presentState(state)); + stdio.printStatus(state_import.presentState(state)); + } + + /// Save the release's [state]. + /// + /// This can be overridden by frontends that may not persist the state to + /// disk, and/or may need to call additional update hooks each time the state + /// is updated. + @visibleForOverriding + void updateState(pb.ConductorState state, List logs) { + state_import.writeStateToFile(stateFile, state, logs); } // To minimize merge conflicts, sort the commits by rev-list order.