Unverified Commit a82255b5 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

[flutter_conductor] updates (#86452)

parent 6406c6d4
...@@ -37,4 +37,7 @@ BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" ...@@ -37,4 +37,7 @@ BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
REPO_DIR="$BIN_DIR/../../.." REPO_DIR="$BIN_DIR/../../.."
DART_BIN="$REPO_DIR/bin/dart" DART_BIN="$REPO_DIR/bin/dart"
# Ensure pub get has been run in the repo before running the conductor
(cd "$REPO_DIR/dev/conductor"; $DART_BIN pub get 1>&2)
"$DART_BIN" --enable-asserts "$REPO_DIR/dev/conductor/bin/conductor.dart" "$@" "$DART_BIN" --enable-asserts "$REPO_DIR/dev/conductor/bin/conductor.dart" "$@"
...@@ -85,11 +85,10 @@ String? getValueFromEnvOrArgs( ...@@ -85,11 +85,10 @@ String? getValueFromEnvOrArgs(
String name, String name,
ArgResults argResults, ArgResults argResults,
Map<String, String> env, { Map<String, String> env, {
bool allowNull = false, bool allowNull = false,
} }) {
) {
final String envName = fromArgToEnvName(name); final String envName = fromArgToEnvName(name);
if (env[envName] != null ) { if (env[envName] != null) {
return env[envName]; return env[envName];
} }
final String? argValue = argResults[name] as String?; final String? argValue = argResults[name] as String?;
...@@ -139,3 +138,13 @@ List<String> getValuesFromEnvOrArgs( ...@@ -139,3 +138,13 @@ List<String> getValuesFromEnvOrArgs(
String fromArgToEnvName(String argName) { String fromArgToEnvName(String argName) {
return argName.toUpperCase().replaceAll(r'-', r'_'); return argName.toUpperCase().replaceAll(r'-', r'_');
} }
/// Return a web link for the user to open a new PR.
String getNewPrLink({
required String userName,
required String repoName,
required String candidateBranch,
required String workingBranch,
}) {
return 'https://github.com/flutter/$repoName/compare/$candidateBranch...$userName:$workingBranch?expand=1';
}
...@@ -102,7 +102,7 @@ void runNext({ ...@@ -102,7 +102,7 @@ void runNext({
} }
} }
if (state.engine.cherrypicks.isEmpty && state.engine.dartRevision.isEmpty) { if (!requiresEnginePR(state)) {
stdio.printStatus( stdio.printStatus(
'This release has no engine cherrypicks. No Engine PR is necessary.\n', 'This release has no engine cherrypicks. No Engine PR is necessary.\n',
); );
...@@ -110,18 +110,16 @@ void runNext({ ...@@ -110,18 +110,16 @@ void runNext({
} }
if (unappliedCherrypicks.isEmpty) { if (unappliedCherrypicks.isEmpty) {
stdio.printStatus('All engine cherrypicks have been auto-applied by ' stdio.printStatus('All engine cherrypicks have been auto-applied by the conductor.\n');
'the conductor.\n');
} else { } else {
stdio.printStatus( stdio.printStatus('There were ${unappliedCherrypicks.length} cherrypicks that were not auto-applied.');
'There were ${unappliedCherrypicks.length} cherrypicks that were not auto-applied.');
stdio.printStatus('These must be applied manually in the directory ' stdio.printStatus('These must be applied manually in the directory '
'${state.engine.checkoutPath} before proceeding.\n'); '${state.engine.checkoutPath} before proceeding.\n');
} }
if (autoAccept == false) { if (autoAccept == false) {
final bool response = prompt( final bool response = prompt(
'Are you ready to push your engine branch to the repository ' 'Are you ready to push your engine branch to the repository '
'${state.engine.mirror.url}?', '${state.engine.mirror.url}?',
stdio, stdio,
); );
if (!response) { if (!response) {
...@@ -189,13 +187,15 @@ void runNext({ ...@@ -189,13 +187,15 @@ void runNext({
break; break;
} }
} }
final Remote engineUpstreamRemote = Remote(
name: RemoteName.upstream,
url: state.engine.upstream.url,
);
final EngineRepository engine = EngineRepository( final EngineRepository engine = EngineRepository(
checkouts, checkouts,
initialRef: state.engine.candidateBranch, // We explicitly want to check out the merged version from upstream
upstreamRemote: Remote( initialRef: '${engineUpstreamRemote.name}/${state.engine.candidateBranch}',
name: RemoteName.upstream, upstreamRemote: engineUpstreamRemote,
url: state.engine.upstream.url,
),
previousCheckoutLocation: state.engine.checkoutPath, previousCheckoutLocation: state.engine.checkoutPath,
); );
...@@ -215,7 +215,9 @@ void runNext({ ...@@ -215,7 +215,9 @@ void runNext({
stdio.printStatus('Rolling new engine hash $engineRevision to framework checkout...'); stdio.printStatus('Rolling new engine hash $engineRevision to framework checkout...');
framework.updateEngineRevision(engineRevision); framework.updateEngineRevision(engineRevision);
framework.commit('Update Engine revision to $engineRevision for ${state.releaseChannel} release ${state.releaseVersion}', addFirst: true); framework.commit(
'Update Engine revision to $engineRevision for ${state.releaseChannel} release ${state.releaseVersion}',
addFirst: true);
if (state.framework.cherrypicks.isEmpty) { if (state.framework.cherrypicks.isEmpty) {
stdio.printStatus( stdio.printStatus(
...@@ -262,7 +264,8 @@ void runNext({ ...@@ -262,7 +264,8 @@ void runNext({
); );
final FrameworkRepository framework = FrameworkRepository( final FrameworkRepository framework = FrameworkRepository(
checkouts, checkouts,
initialRef: state.framework.candidateBranch, // We explicitly want to check out the merged version from upstream
initialRef: '${upstream.name}/${state.framework.candidateBranch}',
upstreamRemote: upstream, upstreamRemote: upstream,
previousCheckoutLocation: state.framework.checkoutPath, previousCheckoutLocation: state.framework.checkoutPath,
); );
...@@ -288,7 +291,8 @@ void runNext({ ...@@ -288,7 +291,8 @@ void runNext({
); );
final FrameworkRepository framework = FrameworkRepository( final FrameworkRepository framework = FrameworkRepository(
checkouts, checkouts,
initialRef: state.framework.candidateBranch, // We explicitly want to check out the merged version from upstream
initialRef: '${upstream.name}/${state.framework.candidateBranch}',
upstreamRemote: upstream, upstreamRemote: upstream,
previousCheckoutLocation: state.framework.checkoutPath, previousCheckoutLocation: state.framework.checkoutPath,
); );
......
...@@ -403,6 +403,8 @@ class ConductorState extends $pb.GeneratedMessage { ...@@ -403,6 +403,8 @@ class ConductorState extends $pb.GeneratedMessage {
enumValues: ReleasePhase.values) enumValues: ReleasePhase.values)
..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'conductorVersion', ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'conductorVersion',
protoName: 'conductorVersion') protoName: 'conductorVersion')
..aOS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'incrementLevel',
protoName: 'incrementLevel')
..hasRequiredFields = false; ..hasRequiredFields = false;
ConductorState._() : super(); ConductorState._() : super();
...@@ -416,6 +418,7 @@ class ConductorState extends $pb.GeneratedMessage { ...@@ -416,6 +418,7 @@ class ConductorState extends $pb.GeneratedMessage {
$core.Iterable<$core.String>? logs, $core.Iterable<$core.String>? logs,
ReleasePhase? currentPhase, ReleasePhase? currentPhase,
$core.String? conductorVersion, $core.String? conductorVersion,
$core.String? incrementLevel,
}) { }) {
final _result = create(); final _result = create();
if (releaseChannel != null) { if (releaseChannel != null) {
...@@ -445,6 +448,9 @@ class ConductorState extends $pb.GeneratedMessage { ...@@ -445,6 +448,9 @@ class ConductorState extends $pb.GeneratedMessage {
if (conductorVersion != null) { if (conductorVersion != null) {
_result.conductorVersion = conductorVersion; _result.conductorVersion = conductorVersion;
} }
if (incrementLevel != null) {
_result.incrementLevel = incrementLevel;
}
return _result; return _result;
} }
factory ConductorState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => factory ConductorState.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
...@@ -572,4 +578,16 @@ class ConductorState extends $pb.GeneratedMessage { ...@@ -572,4 +578,16 @@ class ConductorState extends $pb.GeneratedMessage {
$core.bool hasConductorVersion() => $_has(8); $core.bool hasConductorVersion() => $_has(8);
@$pb.TagNumber(10) @$pb.TagNumber(10)
void clearConductorVersion() => clearField(10); void clearConductorVersion() => clearField(10);
@$pb.TagNumber(11)
$core.String get incrementLevel => $_getSZ(9);
@$pb.TagNumber(11)
set incrementLevel($core.String v) {
$_setString(9, v);
}
@$pb.TagNumber(11)
$core.bool hasIncrementLevel() => $_has(9);
@$pb.TagNumber(11)
void clearIncrementLevel() => clearField(11);
} }
...@@ -101,9 +101,10 @@ const ConductorState$json = const { ...@@ -101,9 +101,10 @@ const ConductorState$json = const {
const {'1': 'logs', '3': 8, '4': 3, '5': 9, '10': 'logs'}, const {'1': 'logs', '3': 8, '4': 3, '5': 9, '10': 'logs'},
const {'1': 'currentPhase', '3': 9, '4': 1, '5': 14, '6': '.conductor_state.ReleasePhase', '10': 'currentPhase'}, const {'1': 'currentPhase', '3': 9, '4': 1, '5': 14, '6': '.conductor_state.ReleasePhase', '10': 'currentPhase'},
const {'1': 'conductorVersion', '3': 10, '4': 1, '5': 9, '10': 'conductorVersion'}, const {'1': 'conductorVersion', '3': 10, '4': 1, '5': 9, '10': 'conductorVersion'},
const {'1': 'incrementLevel', '3': 11, '4': 1, '5': 9, '10': 'incrementLevel'},
], ],
}; };
/// Descriptor for `ConductorState`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `ConductorState`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List conductorStateDescriptor = $convert.base64Decode( final $typed_data.Uint8List conductorStateDescriptor = $convert.base64Decode(
'Cg5Db25kdWN0b3JTdGF0ZRImCg5yZWxlYXNlQ2hhbm5lbBgBIAEoCVIOcmVsZWFzZUNoYW5uZWwSJgoOcmVsZWFzZVZlcnNpb24YAiABKAlSDnJlbGVhc2VWZXJzaW9uEjMKBmVuZ2luZRgEIAEoCzIbLmNvbmR1Y3Rvcl9zdGF0ZS5SZXBvc2l0b3J5UgZlbmdpbmUSOQoJZnJhbWV3b3JrGAUgASgLMhsuY29uZHVjdG9yX3N0YXRlLlJlcG9zaXRvcnlSCWZyYW1ld29yaxIgCgtjcmVhdGVkRGF0ZRgGIAEoA1ILY3JlYXRlZERhdGUSKAoPbGFzdFVwZGF0ZWREYXRlGAcgASgDUg9sYXN0VXBkYXRlZERhdGUSEgoEbG9ncxgIIAMoCVIEbG9ncxJBCgxjdXJyZW50UGhhc2UYCSABKA4yHS5jb25kdWN0b3Jfc3RhdGUuUmVsZWFzZVBoYXNlUgxjdXJyZW50UGhhc2USKgoQY29uZHVjdG9yVmVyc2lvbhgKIAEoCVIQY29uZHVjdG9yVmVyc2lvbg=='); 'Cg5Db25kdWN0b3JTdGF0ZRImCg5yZWxlYXNlQ2hhbm5lbBgBIAEoCVIOcmVsZWFzZUNoYW5uZWwSJgoOcmVsZWFzZVZlcnNpb24YAiABKAlSDnJlbGVhc2VWZXJzaW9uEjMKBmVuZ2luZRgEIAEoCzIbLmNvbmR1Y3Rvcl9zdGF0ZS5SZXBvc2l0b3J5UgZlbmdpbmUSOQoJZnJhbWV3b3JrGAUgASgLMhsuY29uZHVjdG9yX3N0YXRlLlJlcG9zaXRvcnlSCWZyYW1ld29yaxIgCgtjcmVhdGVkRGF0ZRgGIAEoA1ILY3JlYXRlZERhdGUSKAoPbGFzdFVwZGF0ZWREYXRlGAcgASgDUg9sYXN0VXBkYXRlZERhdGUSEgoEbG9ncxgIIAMoCVIEbG9ncxJBCgxjdXJyZW50UGhhc2UYCSABKA4yHS5jb25kdWN0b3Jfc3RhdGUuUmVsZWFzZVBoYXNlUgxjdXJyZW50UGhhc2USKgoQY29uZHVjdG9yVmVyc2lvbhgKIAEoCVIQY29uZHVjdG9yVmVyc2lvbhImCg5pbmNyZW1lbnRMZXZlbBgLIAEoCVIOaW5jcmVtZW50TGV2ZWw=');
...@@ -111,4 +111,7 @@ message ConductorState { ...@@ -111,4 +111,7 @@ message ConductorState {
// Commit hash of the Conductor tool. // Commit hash of the Conductor tool.
string conductorVersion = 10; string conductorVersion = 10;
// One of x, y, z, m, or n.
string incrementLevel = 11;
} }
...@@ -74,8 +74,10 @@ abstract class Repository { ...@@ -74,8 +74,10 @@ abstract class Repository {
'Fetch ${upstreamRemote.name} to ensure we have latest refs', 'Fetch ${upstreamRemote.name} to ensure we have latest refs',
workingDirectory: _checkoutDirectory!.path, workingDirectory: _checkoutDirectory!.path,
); );
// Note: if [initialRef] is a remote ref the checkout will be left in a
// detached HEAD state.
git.run( git.run(
<String>['checkout', '${upstreamRemote.name}/$initialRef'], <String>['checkout', initialRef!],
'Checking out initialRef $initialRef', 'Checking out initialRef $initialRef',
workingDirectory: _checkoutDirectory!.path, workingDirectory: _checkoutDirectory!.path,
); );
......
...@@ -213,6 +213,7 @@ class StartCommand extends Command<void> { ...@@ -213,6 +213,7 @@ class StartCommand extends Command<void> {
state.releaseChannel = releaseChannel; state.releaseChannel = releaseChannel;
state.createdDate = unixDate; state.createdDate = unixDate;
state.lastUpdatedDate = unixDate; state.lastUpdatedDate = unixDate;
state.incrementLevel = incrementLetter;
final EngineRepository engine = EngineRepository( final EngineRepository engine = EngineRepository(
checkouts, checkouts,
......
...@@ -43,10 +43,8 @@ String presentState(pb.ConductorState state) { ...@@ -43,10 +43,8 @@ String presentState(pb.ConductorState state) {
buffer.writeln('Release channel: ${state.releaseChannel}'); buffer.writeln('Release channel: ${state.releaseChannel}');
buffer.writeln('Release version: ${state.releaseVersion}'); buffer.writeln('Release version: ${state.releaseVersion}');
buffer.writeln(''); buffer.writeln('');
buffer.writeln( buffer.writeln('Release started at: ${DateTime.fromMillisecondsSinceEpoch(state.createdDate.toInt())}');
'Release started at: ${DateTime.fromMillisecondsSinceEpoch(state.createdDate.toInt())}'); buffer.writeln('Last updated at: ${DateTime.fromMillisecondsSinceEpoch(state.lastUpdatedDate.toInt())}');
buffer.writeln(
'Last updated at: ${DateTime.fromMillisecondsSinceEpoch(state.lastUpdatedDate.toInt())}');
buffer.writeln(''); buffer.writeln('');
buffer.writeln('Engine Repo'); buffer.writeln('Engine Repo');
buffer.writeln('\tCandidate branch: ${state.engine.candidateBranch}'); buffer.writeln('\tCandidate branch: ${state.engine.candidateBranch}');
...@@ -128,15 +126,27 @@ String phaseInstructions(pb.ConductorState state) { ...@@ -128,15 +126,27 @@ String phaseInstructions(pb.ConductorState state) {
return <String>[ return <String>[
'You must now manually apply the following engine cherrypicks to the checkout', 'You must now manually apply the following engine cherrypicks to the checkout',
'at ${state.engine.checkoutPath} in order:', 'at ${state.engine.checkoutPath} in order:',
for (final pb.Cherrypick cherrypick in state.engine.cherrypicks) for (final pb.Cherrypick cherrypick in state.engine.cherrypicks) '\t${cherrypick.trunkRevision}',
'\t${cherrypick.trunkRevision}',
'See $kReleaseDocumentationUrl for more information.', 'See $kReleaseDocumentationUrl for more information.',
].join('\n'); ].join('\n');
case ReleasePhase.CODESIGN_ENGINE_BINARIES: case ReleasePhase.CODESIGN_ENGINE_BINARIES:
if (!requiresEnginePR(state)) {
return 'You must now codesign the engine binaries for commit '
'${state.engine.startingGitHead}.';
}
// User's working branch was pushed to their mirror, but a PR needs to be
// opened on GitHub.
final String newPrLink = getNewPrLink(
userName: githubAccount(state.engine.mirror.url),
repoName: 'engine',
candidateBranch: state.engine.candidateBranch,
workingBranch: state.engine.workingBranch,
);
return <String>[ return <String>[
'You must verify pre-submit CI builds on your engine pull request are successful,', 'Your working branch ${state.engine.workingBranch} was pushed to your mirror.',
'merge your pull request, validate post-submit CI, and then codesign the binaries ', 'You must now open a pull request at $newPrLink, verify pre-submit CI',
'on the merge commit.', 'builds on your engine pull request are successful, merge your pull request,',
'validate post-submit CI, and then codesign the binaries on the merge commit.',
].join('\n'); ].join('\n');
case ReleasePhase.APPLY_FRAMEWORK_CHERRYPICKS: case ReleasePhase.APPLY_FRAMEWORK_CHERRYPICKS:
final List<pb.Cherrypick> outstandingCherrypicks = state.framework.cherrypicks.where( final List<pb.Cherrypick> outstandingCherrypicks = state.framework.cherrypicks.where(
...@@ -144,17 +154,31 @@ String phaseInstructions(pb.ConductorState state) { ...@@ -144,17 +154,31 @@ String phaseInstructions(pb.ConductorState state) {
return cp.state == pb.CherrypickState.PENDING || cp.state == pb.CherrypickState.PENDING_WITH_CONFLICT; return cp.state == pb.CherrypickState.PENDING || cp.state == pb.CherrypickState.PENDING_WITH_CONFLICT;
}, },
).toList(); ).toList();
return <String>[ if (outstandingCherrypicks.isNotEmpty) {
'You must now manually apply the following framework cherrypicks to the checkout', return <String>[
'at ${state.framework.checkoutPath} in order:', 'You must now manually apply the following framework cherrypicks to the checkout',
for (final pb.Cherrypick cherrypick in outstandingCherrypicks) 'at ${state.framework.checkoutPath} in order:',
'\t${cherrypick.trunkRevision}', for (final pb.Cherrypick cherrypick in outstandingCherrypicks) '\t${cherrypick.trunkRevision}',
].join('\n'); ].join('\n');
}
return <String>['Either all cherrypicks have been auto-applied or there were none.'].join('\n');
case ReleasePhase.PUBLISH_VERSION: case ReleasePhase.PUBLISH_VERSION:
if (!requiresFrameworkPR(state)) {
return 'Since there are no code changes in this release, no Framework '
'PR is necessary.';
}
final String newPrLink = getNewPrLink(
userName: githubAccount(state.framework.mirror.url),
repoName: 'framework',
candidateBranch: state.framework.candidateBranch,
workingBranch: state.framework.workingBranch,
);
return <String>[ return <String>[
'You must verify pre-submit CI builds on your framework pull request are successful,', 'Your working branch ${state.framework.workingBranch} was pushed to your mirror.',
'merge your pull request, and validate post-submit CI. See $kReleaseDocumentationUrl,', 'You must now open a pull request at $newPrLink',
'for more information.', 'verify pre-submit CI builds on your pull request are successful, merge your ',
'pull request, validate post-submit CI.',
].join('\n'); ].join('\n');
case ReleasePhase.PUBLISH_CHANNEL: case ReleasePhase.PUBLISH_CHANNEL:
return 'Issue `conductor next` to publish your release to the release branch.'; return 'Issue `conductor next` to publish your release to the release branch.';
...@@ -163,8 +187,36 @@ String phaseInstructions(pb.ConductorState state) { ...@@ -163,8 +187,36 @@ String phaseInstructions(pb.ConductorState state) {
case ReleasePhase.RELEASE_COMPLETED: case ReleasePhase.RELEASE_COMPLETED:
return 'This release has been completed.'; return 'This release has been completed.';
} }
assert(false); // For analyzer
return ''; // For analyzer throw ConductorException('Unimplemented phase ${state.currentPhase}');
}
/// Regex pattern for git remote host URLs.
///
/// First group = git host (currently must be github.com)
/// Second group = account name
/// Third group = repo name
final RegExp githubRemotePattern =
RegExp(r'^(git@github\.com:|https?:\/\/github\.com\/)([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+)(\.git)?$');
/// Parses a Git remote URL and returns the account name.
///
/// Uses [githubRemotePattern].
String githubAccount(String remoteUrl) {
final String engineUrl = remoteUrl;
final RegExpMatch? match = githubRemotePattern.firstMatch(engineUrl);
if (match == null) {
throw ConductorException(
'Cannot determine the GitHub account from $engineUrl',
);
}
final String? accountName = match.group(2);
if (accountName == null || accountName.isEmpty) {
throw ConductorException(
'Cannot determine the GitHub account from $match',
);
}
return accountName;
} }
/// Returns the next phase in the ReleasePhase enum. /// Returns the next phase in the ReleasePhase enum.
...@@ -196,3 +248,38 @@ pb.ConductorState readStateFromFile(File file) { ...@@ -196,3 +248,38 @@ pb.ConductorState readStateFromFile(File file) {
); );
return state; return state;
} }
/// This release will require a new Engine PR.
///
/// The logic is if there are engine cherrypicks that have not been abandoned OR
/// there is a new Dart revision, then return true, else false.
bool requiresEnginePR(pb.ConductorState state) {
final bool hasRequiredCherrypicks = state.engine.cherrypicks.any(
(pb.Cherrypick cp) => cp.state != pb.CherrypickState.ABANDONED,
);
if (hasRequiredCherrypicks) {
return true;
}
return state.engine.dartRevision.isNotEmpty;
}
/// This release will require a new Framework PR.
///
/// The logic is if there was an Engine PR OR there are framework cherrypicks
/// that have not been abandoned OR the increment level is 'm', then return
/// true, else false.
bool requiresFrameworkPR(pb.ConductorState state) {
if (requiresEnginePR(state)) {
return true;
}
final bool hasRequiredCherrypicks =
state.framework.cherrypicks.any((pb.Cherrypick cp) => cp.state != pb.CherrypickState.ABANDONED);
if (hasRequiredCherrypicks) {
return true;
}
if (state.incrementLevel == 'm') {
// requires an update to .ci.yaml
return true;
}
return false;
}
...@@ -46,10 +46,10 @@ class VerboseStdio extends Stdio { ...@@ -46,10 +46,10 @@ class VerboseStdio extends Stdio {
}); });
factory VerboseStdio.local() => VerboseStdio( factory VerboseStdio.local() => VerboseStdio(
stdout: io.stdout, stdout: io.stdout,
stderr: io.stderr, stderr: io.stderr,
stdin: io.stdin, stdin: io.stdin,
); );
final io.Stdout stdout; final io.Stdout stdout;
final io.Stdout stderr; final io.Stdout stderr;
......
...@@ -89,6 +89,9 @@ void main() { ...@@ -89,6 +89,9 @@ void main() {
); );
final pb.ConductorState state = pb.ConductorState( final pb.ConductorState state = pb.ConductorState(
currentPhase: ReleasePhase.APPLY_ENGINE_CHERRYPICKS, currentPhase: ReleasePhase.APPLY_ENGINE_CHERRYPICKS,
engine: pb.Repository(
startingGitHead: revision1,
),
); );
writeStateToFile( writeStateToFile(
fileSystem.file(stateFile), fileSystem.file(stateFile),
...@@ -116,10 +119,13 @@ void main() { ...@@ -116,10 +119,13 @@ void main() {
expect(processManager, hasNoRemainingExpectations); expect(processManager, hasNoRemainingExpectations);
expect(finalState.currentPhase, ReleasePhase.CODESIGN_ENGINE_BINARIES); expect(finalState.currentPhase, ReleasePhase.CODESIGN_ENGINE_BINARIES);
expect(stdio.error, isEmpty); expect(stdio.error, isEmpty);
expect(
stdio.stdout,
contains('You must now codesign the engine binaries for commit $revision1'));
}); });
test('confirms to stdout when all engine cherrypicks were auto-applied', () async { test('confirms to stdout when all engine cherrypicks were auto-applied', () async {
const String remoteUrl = 'https://githost.com/org/repo.git'; const String remoteUrl = 'https://github.com/org/repo.git';
stdio.stdin.add('n'); stdio.stdin.add('n');
final FakeProcessManager processManager = FakeProcessManager.empty(); final FakeProcessManager processManager = FakeProcessManager.empty();
final FakePlatform platform = FakePlatform( final FakePlatform platform = FakePlatform(
...@@ -170,13 +176,14 @@ void main() { ...@@ -170,13 +176,14 @@ void main() {
}); });
test('updates lastPhase if user responds yes', () async { test('updates lastPhase if user responds yes', () async {
const String remoteUrl = 'https://githost.com/org/repo.git'; const String remoteUrl = 'https://github.com/org/repo.git';
const String releaseChannel = 'dev';
stdio.stdin.add('y'); stdio.stdin.add('y');
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand( const FakeCommand(
command: <String>['git', 'fetch', 'upstream'], command: <String>['git', 'fetch', 'upstream'],
), ),
const FakeCommand(command: <String>['git', 'checkout', 'upstream/$workingBranch']), const FakeCommand(command: <String>['git', 'checkout', workingBranch]),
const FakeCommand( const FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision1, stdout: revision1,
...@@ -191,7 +198,9 @@ void main() { ...@@ -191,7 +198,9 @@ void main() {
pathSeparator: localPathSeparator, pathSeparator: localPathSeparator,
); );
final pb.ConductorState state = pb.ConductorState( final pb.ConductorState state = pb.ConductorState(
currentPhase: ReleasePhase.APPLY_ENGINE_CHERRYPICKS,
engine: pb.Repository( engine: pb.Repository(
candidateBranch: candidateBranch,
cherrypicks: <pb.Cherrypick>[ cherrypicks: <pb.Cherrypick>[
pb.Cherrypick( pb.Cherrypick(
trunkRevision: 'abc123', trunkRevision: 'abc123',
...@@ -202,7 +211,7 @@ void main() { ...@@ -202,7 +211,7 @@ void main() {
upstream: pb.Remote(name: 'upstream', url: remoteUrl), upstream: pb.Remote(name: 'upstream', url: remoteUrl),
mirror: pb.Remote(name: 'mirror', url: remoteUrl), mirror: pb.Remote(name: 'mirror', url: remoteUrl),
), ),
currentPhase: ReleasePhase.APPLY_ENGINE_CHERRYPICKS, releaseChannel: releaseChannel,
); );
writeStateToFile( writeStateToFile(
fileSystem.file(stateFile), fileSystem.file(stateFile),
...@@ -228,6 +237,9 @@ void main() { ...@@ -228,6 +237,9 @@ void main() {
); );
expect(processManager, hasNoRemainingExpectations); expect(processManager, hasNoRemainingExpectations);
expect(
stdio.stdout,
contains('You must now open a pull request at https://github.com/flutter/engine/compare/flutter-1.2-candidate.3...org:cherrypicks-flutter-1.2-candidate.3?expand=1'));
expect(stdio.stdout, contains( expect(stdio.stdout, contains(
'Are you ready to push your engine branch to the repository $remoteUrl? (y/n) ')); 'Are you ready to push your engine branch to the repository $remoteUrl? (y/n) '));
expect(finalState.currentPhase, ReleasePhase.CODESIGN_ENGINE_BINARIES); expect(finalState.currentPhase, ReleasePhase.CODESIGN_ENGINE_BINARIES);
...@@ -332,9 +344,9 @@ void main() { ...@@ -332,9 +344,9 @@ void main() {
}); });
group('APPLY_FRAMEWORK_CHERRYPICKS to PUBLISH_VERSION', () { group('APPLY_FRAMEWORK_CHERRYPICKS to PUBLISH_VERSION', () {
const String mirrorRemoteUrl = 'https://githost.com/org/repo.git'; const String mirrorRemoteUrl = 'https://github.com/org/repo.git';
const String upstreamRemoteUrl = 'https://githost.com/mirror/repo.git'; const String upstreamRemoteUrl = 'https://github.com/mirror/repo.git';
const String engineUpstreamRemoteUrl = 'https://githost.com/mirror/engine.git'; const String engineUpstreamRemoteUrl = 'https://github.com/mirror/engine.git';
const String frameworkCheckoutPath = '$checkoutsParentDirectory/framework'; const String frameworkCheckoutPath = '$checkoutsParentDirectory/framework';
const String engineCheckoutPath = '$checkoutsParentDirectory/engine'; const String engineCheckoutPath = '$checkoutsParentDirectory/engine';
const String oldEngineVersion = '000000001'; const String oldEngineVersion = '000000001';
...@@ -441,13 +453,14 @@ void main() { ...@@ -441,13 +453,14 @@ void main() {
stdio.stdin.add('n'); stdio.stdin.add('n');
processManager.addCommands(const <FakeCommand>[ processManager.addCommands(const <FakeCommand>[
FakeCommand(command: <String>['git', 'fetch', 'upstream']), FakeCommand(command: <String>['git', 'fetch', 'upstream']),
// we want merged upstream commit, not local working commit
FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']), FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
FakeCommand( FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision1, stdout: revision1,
), ),
FakeCommand(command: <String>['git', 'fetch', 'upstream']), FakeCommand(command: <String>['git', 'fetch', 'upstream']),
FakeCommand(command: <String>['git', 'checkout', 'upstream/$workingBranch']), FakeCommand(command: <String>['git', 'checkout', workingBranch]),
FakeCommand( FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2, stdout: revision2,
...@@ -509,13 +522,14 @@ void main() { ...@@ -509,13 +522,14 @@ void main() {
stdio.stdin.add('n'); stdio.stdin.add('n');
processManager.addCommands(const <FakeCommand>[ processManager.addCommands(const <FakeCommand>[
FakeCommand(command: <String>['git', 'fetch', 'upstream']), FakeCommand(command: <String>['git', 'fetch', 'upstream']),
// we want merged upstream commit, not local working commit
FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']), FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
FakeCommand( FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision1, stdout: revision1,
), ),
FakeCommand(command: <String>['git', 'fetch', 'upstream']), FakeCommand(command: <String>['git', 'fetch', 'upstream']),
FakeCommand(command: <String>['git', 'checkout', 'upstream/$workingBranch']), FakeCommand(command: <String>['git', 'checkout', workingBranch]),
FakeCommand( FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2, stdout: revision2,
...@@ -564,6 +578,7 @@ void main() { ...@@ -564,6 +578,7 @@ void main() {
processManager.addCommands(const <FakeCommand>[ processManager.addCommands(const <FakeCommand>[
// Engine repo // Engine repo
FakeCommand(command: <String>['git', 'fetch', 'upstream']), FakeCommand(command: <String>['git', 'fetch', 'upstream']),
// we want merged upstream commit, not local working commit
FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']), FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
FakeCommand( FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
...@@ -571,7 +586,7 @@ void main() { ...@@ -571,7 +586,7 @@ void main() {
), ),
// Framework repo // Framework repo
FakeCommand(command: <String>['git', 'fetch', 'upstream']), FakeCommand(command: <String>['git', 'fetch', 'upstream']),
FakeCommand(command: <String>['git', 'checkout', 'upstream/$workingBranch']), FakeCommand(command: <String>['git', 'checkout', workingBranch]),
FakeCommand( FakeCommand(
command: <String>['git', 'rev-parse', 'HEAD'], command: <String>['git', 'rev-parse', 'HEAD'],
stdout: revision2, stdout: revision2,
......
...@@ -115,6 +115,7 @@ void main() { ...@@ -115,6 +115,7 @@ void main() {
const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e'; const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
const String previousVersion = '1.2.0-1.0.pre'; const String previousVersion = '1.2.0-1.0.pre';
const String nextVersion = '1.2.0-3.0.pre'; const String nextVersion = '1.2.0-3.0.pre';
const String incrementLevel = 'm';
final Directory engine = fileSystem.directory(checkoutsParentDirectory) final Directory engine = fileSystem.directory(checkoutsParentDirectory)
.childDirectory('flutter_conductor_checkouts') .childDirectory('flutter_conductor_checkouts')
...@@ -261,7 +262,7 @@ void main() { ...@@ -261,7 +262,7 @@ void main() {
'--$kDartRevisionOption', '--$kDartRevisionOption',
nextDartRevision, nextDartRevision,
'--$kIncrementOption', '--$kIncrementOption',
'm', incrementLevel,
]); ]);
final File stateFile = fileSystem.file(stateFilePath); final File stateFile = fileSystem.file(stateFilePath);
...@@ -281,6 +282,7 @@ void main() { ...@@ -281,6 +282,7 @@ void main() {
expect(state.framework.startingGitHead, revision3); expect(state.framework.startingGitHead, revision3);
expect(state.currentPhase, ReleasePhase.APPLY_ENGINE_CHERRYPICKS); expect(state.currentPhase, ReleasePhase.APPLY_ENGINE_CHERRYPICKS);
expect(state.conductorVersion, revision); expect(state.conductorVersion, revision);
expect(state.incrementLevel, incrementLevel);
}); });
}, onPlatform: <String, dynamic>{ }, onPlatform: <String, dynamic>{
'windows': const Skip('Flutter Conductor only supported on macos/linux'), 'windows': const Skip('Flutter Conductor only supported on macos/linux'),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment