[flutter_conductor] fix pushing local working branch to mirror remote (#88057)
This commit is contained in:
parent
c91a298249
commit
1f1065d5f8
@ -182,19 +182,25 @@ String getNewPrLink({
|
|||||||
if (repoName == 'engine') {
|
if (repoName == 'engine') {
|
||||||
if (state.engine.dartRevision.isNotEmpty) {
|
if (state.engine.dartRevision.isNotEmpty) {
|
||||||
// shorten hashes to make final link manageable
|
// shorten hashes to make final link manageable
|
||||||
|
// prefix with github org/repo so GitHub will auto-generate a hyperlink
|
||||||
body.writeln('- Roll dart revision: dart-lang/sdk@${state.engine.dartRevision.substring(0, 9)}');
|
body.writeln('- Roll dart revision: dart-lang/sdk@${state.engine.dartRevision.substring(0, 9)}');
|
||||||
}
|
}
|
||||||
body.writeAll(
|
for (final pb.Cherrypick cp in state.engine.cherrypicks) {
|
||||||
state.engine.cherrypicks.map<String>((pb.Cherrypick cp) => '- commit: ${cp.trunkRevision.substring(0, 9)}'),
|
// Only list commits that map to a commit that exists upstream.
|
||||||
'\n',
|
if (cp.trunkRevision.isNotEmpty) {
|
||||||
);
|
body.writeln('- commit: flutter/engine@${cp.trunkRevision.substring(0, 9)}');
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
body.writeAll(
|
for (final pb.Cherrypick cp in state.framework.cherrypicks) {
|
||||||
state.framework.cherrypicks.map<String>((pb.Cherrypick cp) => '- commit: ${cp.trunkRevision.substring(0, 9)}'),
|
// Only list commits that map to a commit that exists upstream.
|
||||||
'\n',
|
if (cp.trunkRevision.isNotEmpty) {
|
||||||
);
|
body.writeln('- commit: ${cp.trunkRevision.substring(0, 9)}');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 'https://github.com/flutter/$repoName/compare/$candidateBranch...$userName:$workingBranch?'
|
return 'https://github.com/flutter/$repoName/compare/'
|
||||||
|
'$candidateBranch...$userName:$workingBranch?'
|
||||||
'expand=1'
|
'expand=1'
|
||||||
'&title=${Uri.encodeQueryComponent(title)}'
|
'&title=${Uri.encodeQueryComponent(title)}'
|
||||||
'&body=${Uri.encodeQueryComponent(body.toString())}';
|
'&body=${Uri.encodeQueryComponent(body.toString())}';
|
||||||
|
@ -95,11 +95,31 @@ void runNext({
|
|||||||
|
|
||||||
switch (state.currentPhase) {
|
switch (state.currentPhase) {
|
||||||
case pb.ReleasePhase.APPLY_ENGINE_CHERRYPICKS:
|
case pb.ReleasePhase.APPLY_ENGINE_CHERRYPICKS:
|
||||||
final List<pb.Cherrypick> unappliedCherrypicks = <pb.Cherrypick>[];
|
final Remote upstream = Remote(
|
||||||
for (final pb.Cherrypick cherrypick in state.engine.cherrypicks) {
|
name: RemoteName.upstream,
|
||||||
if (!finishedStates.contains(cherrypick.state)) {
|
url: state.engine.upstream.url,
|
||||||
unappliedCherrypicks.add(cherrypick);
|
);
|
||||||
}
|
final EngineRepository engine = EngineRepository(
|
||||||
|
checkouts,
|
||||||
|
initialRef: state.engine.workingBranch,
|
||||||
|
upstreamRemote: upstream,
|
||||||
|
previousCheckoutLocation: state.engine.checkoutPath,
|
||||||
|
);
|
||||||
|
final String headRevision = engine.reverseParse('HEAD');
|
||||||
|
|
||||||
|
// check if the candidate branch is enabled in .ci.yaml
|
||||||
|
if (!engine.ciYaml.enabledBranches.contains(state.engine.candidateBranch)) {
|
||||||
|
engine.ciYaml.enableBranch(state.engine.candidateBranch);
|
||||||
|
// commit
|
||||||
|
final String revision = engine.commit(
|
||||||
|
'add branch ${state.engine.candidateBranch} to enabled_branches in .ci.yaml',
|
||||||
|
addFirst: true,
|
||||||
|
);
|
||||||
|
// append to list of cherrypicks so we know a PR is required
|
||||||
|
state.engine.cherrypicks.add(pb.Cherrypick(
|
||||||
|
appliedRevision: revision,
|
||||||
|
state: pb.CherrypickState.COMPLETED,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requiresEnginePR(state)) {
|
if (!requiresEnginePR(state)) {
|
||||||
@ -109,6 +129,13 @@ void runNext({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<pb.Cherrypick> unappliedCherrypicks = <pb.Cherrypick>[];
|
||||||
|
for (final pb.Cherrypick cherrypick in state.engine.cherrypicks) {
|
||||||
|
if (!finishedStates.contains(cherrypick.state)) {
|
||||||
|
unappliedCherrypicks.add(cherrypick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (unappliedCherrypicks.isEmpty) {
|
if (unappliedCherrypicks.isEmpty) {
|
||||||
stdio.printStatus('All engine cherrypicks have been auto-applied by the conductor.\n');
|
stdio.printStatus('All engine cherrypicks have been auto-applied by the conductor.\n');
|
||||||
} else {
|
} else {
|
||||||
@ -128,21 +155,11 @@ void runNext({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Remote upstream = Remote(
|
|
||||||
name: RemoteName.upstream,
|
|
||||||
url: state.engine.upstream.url,
|
|
||||||
);
|
|
||||||
final EngineRepository engine = EngineRepository(
|
|
||||||
checkouts,
|
|
||||||
initialRef: state.engine.workingBranch,
|
|
||||||
upstreamRemote: upstream,
|
|
||||||
previousCheckoutLocation: state.engine.checkoutPath,
|
|
||||||
);
|
|
||||||
final String headRevision = engine.reverseParse('HEAD');
|
|
||||||
|
|
||||||
engine.pushRef(
|
engine.pushRef(
|
||||||
fromRef: headRevision,
|
fromRef: headRevision,
|
||||||
toRef: state.engine.workingBranch,
|
// Explicitly create new branch
|
||||||
|
toRef: 'refs/heads/${state.engine.workingBranch}',
|
||||||
remote: state.engine.mirror.name,
|
remote: state.engine.mirror.name,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -166,13 +183,6 @@ void runNext({
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case pb.ReleasePhase.APPLY_FRAMEWORK_CHERRYPICKS:
|
case pb.ReleasePhase.APPLY_FRAMEWORK_CHERRYPICKS:
|
||||||
final List<pb.Cherrypick> unappliedCherrypicks = <pb.Cherrypick>[];
|
|
||||||
for (final pb.Cherrypick cherrypick in state.framework.cherrypicks) {
|
|
||||||
if (!finishedStates.contains(cherrypick.state)) {
|
|
||||||
unappliedCherrypicks.add(cherrypick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.engine.cherrypicks.isEmpty && state.engine.dartRevision.isEmpty) {
|
if (state.engine.cherrypicks.isEmpty && state.engine.dartRevision.isEmpty) {
|
||||||
stdio.printStatus(
|
stdio.printStatus(
|
||||||
'This release has no engine cherrypicks, and thus the engine.version file\n'
|
'This release has no engine cherrypicks, and thus the engine.version file\n'
|
||||||
@ -213,11 +223,41 @@ void runNext({
|
|||||||
);
|
);
|
||||||
final String headRevision = framework.reverseParse('HEAD');
|
final String headRevision = framework.reverseParse('HEAD');
|
||||||
|
|
||||||
|
// Check if the current candidate branch is enabled
|
||||||
|
if (!framework.ciYaml.enabledBranches.contains(state.framework.candidateBranch)) {
|
||||||
|
framework.ciYaml.enableBranch(state.framework.candidateBranch);
|
||||||
|
// commit
|
||||||
|
final String revision = framework.commit(
|
||||||
|
'add branch ${state.framework.candidateBranch} to enabled_branches in .ci.yaml',
|
||||||
|
addFirst: true,
|
||||||
|
);
|
||||||
|
// append to list of cherrypicks so we know a PR is required
|
||||||
|
state.framework.cherrypicks.add(pb.Cherrypick(
|
||||||
|
appliedRevision: revision,
|
||||||
|
state: pb.CherrypickState.COMPLETED,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
stdio.printStatus('Rolling new engine hash $engineRevision to framework checkout...');
|
stdio.printStatus('Rolling new engine hash $engineRevision to framework checkout...');
|
||||||
framework.updateEngineRevision(engineRevision);
|
final bool needsCommit = framework.updateEngineRevision(engineRevision);
|
||||||
framework.commit(
|
if (needsCommit) {
|
||||||
|
final String revision = framework.commit(
|
||||||
'Update Engine revision to $engineRevision for ${state.releaseChannel} release ${state.releaseVersion}',
|
'Update Engine revision to $engineRevision for ${state.releaseChannel} release ${state.releaseVersion}',
|
||||||
addFirst: true);
|
addFirst: true,
|
||||||
|
);
|
||||||
|
// append to list of cherrypicks so we know a PR is required
|
||||||
|
state.framework.cherrypicks.add(pb.Cherrypick(
|
||||||
|
appliedRevision: revision,
|
||||||
|
state: pb.CherrypickState.COMPLETED,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<pb.Cherrypick> unappliedCherrypicks = <pb.Cherrypick>[];
|
||||||
|
for (final pb.Cherrypick cherrypick in state.framework.cherrypicks) {
|
||||||
|
if (!finishedStates.contains(cherrypick.state)) {
|
||||||
|
unappliedCherrypicks.add(cherrypick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.framework.cherrypicks.isEmpty) {
|
if (state.framework.cherrypicks.isEmpty) {
|
||||||
stdio.printStatus(
|
stdio.printStatus(
|
||||||
@ -251,7 +291,8 @@ void runNext({
|
|||||||
|
|
||||||
framework.pushRef(
|
framework.pushRef(
|
||||||
fromRef: headRevision,
|
fromRef: headRevision,
|
||||||
toRef: state.framework.workingBranch,
|
// Explicitly create new branch
|
||||||
|
toRef: 'refs/heads/${state.framework.workingBranch}',
|
||||||
remote: state.framework.mirror.name,
|
remote: state.framework.mirror.name,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -9,6 +9,7 @@ import 'package:file/file.dart';
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
import 'package:process/process.dart';
|
import 'package:process/process.dart';
|
||||||
|
import 'package:yaml/yaml.dart';
|
||||||
|
|
||||||
import './git.dart';
|
import './git.dart';
|
||||||
import './globals.dart';
|
import './globals.dart';
|
||||||
@ -69,6 +70,7 @@ abstract class Repository {
|
|||||||
'Provided previousCheckoutLocation $previousCheckoutLocation does not exist on disk!');
|
'Provided previousCheckoutLocation $previousCheckoutLocation does not exist on disk!');
|
||||||
}
|
}
|
||||||
if (initialRef != null) {
|
if (initialRef != null) {
|
||||||
|
assert(initialRef != '');
|
||||||
git.run(
|
git.run(
|
||||||
<String>['fetch', upstreamRemote.name],
|
<String>['fetch', upstreamRemote.name],
|
||||||
'Fetch ${upstreamRemote.name} to ensure we have latest refs',
|
'Fetch ${upstreamRemote.name} to ensure we have latest refs',
|
||||||
@ -398,6 +400,14 @@ abstract class Repository {
|
|||||||
bool addFirst = false,
|
bool addFirst = false,
|
||||||
}) {
|
}) {
|
||||||
assert(!message.contains("'"));
|
assert(!message.contains("'"));
|
||||||
|
final bool hasChanges = git.getOutput(
|
||||||
|
<String>['status', '--porcelain'],
|
||||||
|
'check for uncommitted changes',
|
||||||
|
workingDirectory: checkoutDirectory.path,
|
||||||
|
).trim().isNotEmpty;
|
||||||
|
if (!hasChanges) {
|
||||||
|
throw ConductorException('Tried to commit with message $message but no changes were present');
|
||||||
|
}
|
||||||
if (addFirst) {
|
if (addFirst) {
|
||||||
git.run(
|
git.run(
|
||||||
<String>['add', '--all'],
|
<String>['add', '--all'],
|
||||||
@ -490,6 +500,7 @@ class FrameworkRepository extends Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Checkouts checkouts;
|
final Checkouts checkouts;
|
||||||
|
late final CiYaml ciYaml = CiYaml(checkoutDirectory.childFile('.ci.yaml'));
|
||||||
static const String defaultUpstream =
|
static const String defaultUpstream =
|
||||||
'https://github.com/flutter/flutter.git';
|
'https://github.com/flutter/flutter.git';
|
||||||
|
|
||||||
@ -586,7 +597,10 @@ class FrameworkRepository extends Repository {
|
|||||||
return Version.fromString(versionJson['frameworkVersion'] as String);
|
return Version.fromString(versionJson['frameworkVersion'] as String);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateEngineRevision(
|
/// Update this framework's engine version file.
|
||||||
|
///
|
||||||
|
/// Returns [true] if the version file was updated and a commit is needed.
|
||||||
|
bool updateEngineRevision(
|
||||||
String newEngine, {
|
String newEngine, {
|
||||||
@visibleForTesting File? engineVersionFile,
|
@visibleForTesting File? engineVersionFile,
|
||||||
}) {
|
}) {
|
||||||
@ -597,8 +611,19 @@ class FrameworkRepository extends Repository {
|
|||||||
.childFile('engine.version');
|
.childFile('engine.version');
|
||||||
assert(engineVersionFile.existsSync());
|
assert(engineVersionFile.existsSync());
|
||||||
final String oldEngine = engineVersionFile.readAsStringSync();
|
final String oldEngine = engineVersionFile.readAsStringSync();
|
||||||
|
if (oldEngine.trim() == newEngine.trim()) {
|
||||||
|
stdio.printTrace(
|
||||||
|
'Tried to update the engine revision but version file is already up to date at: $newEngine',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
stdio.printStatus('Updating engine revision from $oldEngine to $newEngine');
|
stdio.printStatus('Updating engine revision from $oldEngine to $newEngine');
|
||||||
engineVersionFile.writeAsStringSync(newEngine.trim(), flush: true);
|
engineVersionFile.writeAsStringSync(
|
||||||
|
// Version files have trailing newlines
|
||||||
|
'${newEngine.trim()}\n',
|
||||||
|
flush: true,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,6 +724,8 @@ class EngineRepository extends Repository {
|
|||||||
|
|
||||||
final Checkouts checkouts;
|
final Checkouts checkouts;
|
||||||
|
|
||||||
|
late final CiYaml ciYaml = CiYaml(checkoutDirectory.childFile('.ci.yaml'));
|
||||||
|
|
||||||
static const String defaultUpstream = 'https://github.com/flutter/engine.git';
|
static const String defaultUpstream = 'https://github.com/flutter/engine.git';
|
||||||
static const String defaultBranch = 'master';
|
static const String defaultBranch = 'master';
|
||||||
|
|
||||||
@ -766,3 +793,68 @@ class Checkouts {
|
|||||||
final ProcessManager processManager;
|
final ProcessManager processManager;
|
||||||
final Stdio stdio;
|
final Stdio stdio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CiYaml {
|
||||||
|
CiYaml(this.file) {
|
||||||
|
if (!file.existsSync()) {
|
||||||
|
throw ConductorException('Could not find the .ci.yaml file at ${file.path}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Underlying [File] that this object wraps.
|
||||||
|
final File file;
|
||||||
|
|
||||||
|
/// Returns the raw string contents of this file.
|
||||||
|
///
|
||||||
|
/// This is not cached as the contents can be written to while the conductor
|
||||||
|
/// is running.
|
||||||
|
String get stringContents => file.readAsStringSync();
|
||||||
|
|
||||||
|
/// Returns the parsed contents of the file as a [YamlMap].
|
||||||
|
///
|
||||||
|
/// This is not cached as the contents can be written to while the conductor
|
||||||
|
/// is running.
|
||||||
|
YamlMap get contents => loadYaml(stringContents) as YamlMap;
|
||||||
|
|
||||||
|
List<String> get enabledBranches {
|
||||||
|
final YamlList yamlList = contents['enabled_branches'] as YamlList;
|
||||||
|
return yamlList.map<String>((dynamic element) {
|
||||||
|
return element as String;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final RegExp _enabledBranchPattern = RegExp(r'^enabled_branches:');
|
||||||
|
|
||||||
|
/// Update this .ci.yaml file with the given branch name.
|
||||||
|
///
|
||||||
|
/// The underlying [File] is written to, but not committed to git. This method
|
||||||
|
/// will throw a [ConductorException] if the [branchName] is already present
|
||||||
|
/// in the file or if the file does not have an "enabled_branches:" field.
|
||||||
|
void enableBranch(String branchName) {
|
||||||
|
final List<String> newStrings = <String>[];
|
||||||
|
if (enabledBranches.contains(branchName)) {
|
||||||
|
throw ConductorException('${file.path} already contains the branch $branchName');
|
||||||
|
}
|
||||||
|
if (!_enabledBranchPattern.hasMatch(stringContents)) {
|
||||||
|
throw ConductorException(
|
||||||
|
'Did not find the expected string "enabled_branches:" in the file ${file.path}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final List<String> lines = stringContents.split('\n');
|
||||||
|
bool insertedCurrentBranch = false;
|
||||||
|
for (final String line in lines) {
|
||||||
|
// Every existing line should be copied to the new Yaml
|
||||||
|
newStrings.add(line);
|
||||||
|
if (insertedCurrentBranch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_enabledBranchPattern.hasMatch(line)) {
|
||||||
|
insertedCurrentBranch = true;
|
||||||
|
// Indent two spaces
|
||||||
|
final String indent = ' ' * 2;
|
||||||
|
newStrings.add('$indent- ${branchName.trim()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.writeAsStringSync(newStrings.join('\n'), flush: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -267,8 +267,7 @@ bool requiresEnginePR(pb.ConductorState state) {
|
|||||||
/// This release will require a new Framework PR.
|
/// This release will require a new Framework PR.
|
||||||
///
|
///
|
||||||
/// The logic is if there was an Engine PR OR there are framework cherrypicks
|
/// 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
|
/// that have not been abandoned.
|
||||||
/// true, else false.
|
|
||||||
bool requiresFrameworkPR(pb.ConductorState state) {
|
bool requiresFrameworkPR(pb.ConductorState state) {
|
||||||
if (requiresEnginePR(state)) {
|
if (requiresEnginePR(state)) {
|
||||||
return true;
|
return true;
|
||||||
@ -278,9 +277,5 @@ bool requiresFrameworkPR(pb.ConductorState state) {
|
|||||||
if (hasRequiredCherrypicks) {
|
if (hasRequiredCherrypicks) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (state.incrementLevel == 'm') {
|
|
||||||
// requires an update to .ci.yaml
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -89,8 +89,9 @@ void main() {
|
|||||||
## Scheduled Cherrypicks
|
## Scheduled Cherrypicks
|
||||||
|
|
||||||
- Roll dart revision: dart-lang/sdk@${dartRevision.substring(0, 9)}
|
- Roll dart revision: dart-lang/sdk@${dartRevision.substring(0, 9)}
|
||||||
- commit: ${engineCherrypick1.substring(0, 9)}
|
- commit: flutter/engine@${engineCherrypick1.substring(0, 9)}
|
||||||
- commit: ${engineCherrypick2.substring(0, 9)}''';
|
- commit: flutter/engine@${engineCherrypick2.substring(0, 9)}
|
||||||
|
''';
|
||||||
expect(
|
expect(
|
||||||
Uri.decodeQueryComponent(bodyPattern.firstMatch(link)?.group(1) ?? ''),
|
Uri.decodeQueryComponent(bodyPattern.firstMatch(link)?.group(1) ?? ''),
|
||||||
expectedBody,
|
expectedBody,
|
||||||
@ -120,7 +121,8 @@ void main() {
|
|||||||
|
|
||||||
## Scheduled Cherrypicks
|
## Scheduled Cherrypicks
|
||||||
|
|
||||||
- commit: ${frameworkCherrypick.substring(0, 9)}''';
|
- commit: ${frameworkCherrypick.substring(0, 9)}
|
||||||
|
''';
|
||||||
expect(
|
expect(
|
||||||
Uri.decodeQueryComponent(bodyPattern.firstMatch(link)?.group(1) ?? ''),
|
Uri.decodeQueryComponent(bodyPattern.firstMatch(link)?.group(1) ?? ''),
|
||||||
expectedBody,
|
expectedBody,
|
||||||
|
@ -21,11 +21,13 @@ void main() {
|
|||||||
const String checkoutsParentDirectory = '$flutterRoot/dev/conductor';
|
const String checkoutsParentDirectory = '$flutterRoot/dev/conductor';
|
||||||
const String candidateBranch = 'flutter-1.2-candidate.3';
|
const String candidateBranch = 'flutter-1.2-candidate.3';
|
||||||
const String workingBranch = 'cherrypicks-$candidateBranch';
|
const String workingBranch = 'cherrypicks-$candidateBranch';
|
||||||
|
const String remoteUrl = 'https://github.com/org/repo.git';
|
||||||
final String localPathSeparator = const LocalPlatform().pathSeparator;
|
final String localPathSeparator = const LocalPlatform().pathSeparator;
|
||||||
final String localOperatingSystem = const LocalPlatform().pathSeparator;
|
final String localOperatingSystem = const LocalPlatform().pathSeparator;
|
||||||
const String revision1 = 'd3af60d18e01fcb36e0c0fa06c8502e4935ed095';
|
const String revision1 = 'd3af60d18e01fcb36e0c0fa06c8502e4935ed095';
|
||||||
const String revision2 = 'f99555c1e1392bf2a8135056b9446680c2af4ddf';
|
const String revision2 = 'f99555c1e1392bf2a8135056b9446680c2af4ddf';
|
||||||
const String revision3 = '98a5ca242b9d270ce000b26309b8a3cdc9c89df5';
|
const String revision3 = '98a5ca242b9d270ce000b26309b8a3cdc9c89df5';
|
||||||
|
const String revision4 = '280e23318a0d8341415c66aa32581352a421d974';
|
||||||
const String releaseVersion = '1.2.0-3.0.pre';
|
const String releaseVersion = '1.2.0-3.0.pre';
|
||||||
const String releaseChannel = 'beta';
|
const String releaseChannel = 'beta';
|
||||||
late MemoryFileSystem fileSystem;
|
late MemoryFileSystem fileSystem;
|
||||||
@ -77,9 +79,16 @@ void main() {
|
|||||||
|
|
||||||
group('APPLY_ENGINE_CHERRYPICKS to CODESIGN_ENGINE_BINARIES', () {
|
group('APPLY_ENGINE_CHERRYPICKS to CODESIGN_ENGINE_BINARIES', () {
|
||||||
test('does not prompt user and updates currentPhase if there are no engine cherrypicks', () async {
|
test('does not prompt user and updates currentPhase if there are no engine cherrypicks', () async {
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
<FakeCommand>[],
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
);
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'checkout', workingBranch],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision1,
|
||||||
|
),
|
||||||
|
]);
|
||||||
final FakePlatform platform = FakePlatform(
|
final FakePlatform platform = FakePlatform(
|
||||||
environment: <String, String>{
|
environment: <String, String>{
|
||||||
'HOME': <String>['path', 'to', 'home'].join(localPathSeparator),
|
'HOME': <String>['path', 'to', 'home'].join(localPathSeparator),
|
||||||
@ -87,10 +96,18 @@ void main() {
|
|||||||
operatingSystem: localOperatingSystem,
|
operatingSystem: localOperatingSystem,
|
||||||
pathSeparator: localPathSeparator,
|
pathSeparator: localPathSeparator,
|
||||||
);
|
);
|
||||||
|
final File ciYaml = fileSystem.file('$checkoutsParentDirectory/engine/.ci.yaml')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
// this branch already present in ciYaml
|
||||||
|
_initializeCiYamlFile(ciYaml, enabledBranches: <String>[candidateBranch]);
|
||||||
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(
|
engine: pb.Repository(
|
||||||
|
candidateBranch: candidateBranch,
|
||||||
|
checkoutPath: fileSystem.path.join(checkoutsParentDirectory, 'engine'),
|
||||||
|
workingBranch: workingBranch,
|
||||||
startingGitHead: revision1,
|
startingGitHead: revision1,
|
||||||
|
upstream: pb.Remote(name: 'upstream', url: remoteUrl),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
writeStateToFile(
|
writeStateToFile(
|
||||||
@ -125,9 +142,32 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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://github.com/org/repo.git';
|
|
||||||
stdio.stdin.add('n');
|
stdio.stdin.add('n');
|
||||||
final FakeProcessManager processManager = FakeProcessManager.empty();
|
final File ciYaml = fileSystem.file('$checkoutsParentDirectory/engine/.ci.yaml')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
_initializeCiYamlFile(ciYaml);
|
||||||
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
|
const FakeCommand(command: <String>['git', 'checkout', workingBranch]),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision1,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM blah',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'commit',
|
||||||
|
"--message='add branch $candidateBranch to enabled_branches in .ci.yaml'",
|
||||||
|
]),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision2,
|
||||||
|
),
|
||||||
|
]);
|
||||||
final FakePlatform platform = FakePlatform(
|
final FakePlatform platform = FakePlatform(
|
||||||
environment: <String, String>{
|
environment: <String, String>{
|
||||||
'HOME': <String>['path', 'to', 'home'].join(localPathSeparator),
|
'HOME': <String>['path', 'to', 'home'].join(localPathSeparator),
|
||||||
@ -137,12 +177,14 @@ void main() {
|
|||||||
);
|
);
|
||||||
final pb.ConductorState state = pb.ConductorState(
|
final pb.ConductorState state = pb.ConductorState(
|
||||||
engine: pb.Repository(
|
engine: pb.Repository(
|
||||||
|
candidateBranch: candidateBranch,
|
||||||
cherrypicks: <pb.Cherrypick>[
|
cherrypicks: <pb.Cherrypick>[
|
||||||
pb.Cherrypick(
|
pb.Cherrypick(
|
||||||
trunkRevision: 'abc123',
|
trunkRevision: 'abc123',
|
||||||
state: pb.CherrypickState.COMPLETED,
|
state: pb.CherrypickState.COMPLETED,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
checkoutPath: fileSystem.path.join(checkoutsParentDirectory, 'engine'),
|
||||||
workingBranch: workingBranch,
|
workingBranch: workingBranch,
|
||||||
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),
|
||||||
@ -183,12 +225,29 @@ void main() {
|
|||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'fetch', 'upstream'],
|
command: <String>['git', 'fetch', 'upstream'],
|
||||||
),
|
),
|
||||||
const FakeCommand(command: <String>['git', 'checkout', workingBranch]),
|
FakeCommand(
|
||||||
|
command: const <String>['git', 'checkout', workingBranch],
|
||||||
|
onRun: () {
|
||||||
|
final File file = fileSystem.file('$checkoutsParentDirectory/engine/.ci.yaml')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
_initializeCiYamlFile(file);
|
||||||
|
},
|
||||||
|
),
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision1,
|
stdout: revision1,
|
||||||
),
|
),
|
||||||
const FakeCommand(command: <String>['git', 'push', 'mirror', '$revision1:$workingBranch']),
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM .ci.yaml',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>['git', 'commit', "--message='add branch $candidateBranch to enabled_branches in .ci.yaml'"]),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision2,
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'push', 'mirror', '$revision1:refs/heads/$workingBranch']),
|
||||||
]);
|
]);
|
||||||
final FakePlatform platform = FakePlatform(
|
final FakePlatform platform = FakePlatform(
|
||||||
environment: <String, String>{
|
environment: <String, String>{
|
||||||
@ -201,6 +260,7 @@ void main() {
|
|||||||
currentPhase: ReleasePhase.APPLY_ENGINE_CHERRYPICKS,
|
currentPhase: ReleasePhase.APPLY_ENGINE_CHERRYPICKS,
|
||||||
engine: pb.Repository(
|
engine: pb.Repository(
|
||||||
candidateBranch: candidateBranch,
|
candidateBranch: candidateBranch,
|
||||||
|
checkoutPath: fileSystem.path.join(checkoutsParentDirectory, 'engine'),
|
||||||
cherrypicks: <pb.Cherrypick>[
|
cherrypicks: <pb.Cherrypick>[
|
||||||
pb.Cherrypick(
|
pb.Cherrypick(
|
||||||
trunkRevision: revision2,
|
trunkRevision: revision2,
|
||||||
@ -219,9 +279,11 @@ void main() {
|
|||||||
state,
|
state,
|
||||||
<String>[],
|
<String>[],
|
||||||
);
|
);
|
||||||
|
// engine dir is expected to already exist
|
||||||
|
fileSystem.directory(checkoutsParentDirectory).childDirectory('engine').createSync(recursive: true);
|
||||||
final Checkouts checkouts = Checkouts(
|
final Checkouts checkouts = Checkouts(
|
||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
parentDirectory: fileSystem.directory(checkoutsParentDirectory)..createSync(recursive: true),
|
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
|
||||||
platform: platform,
|
platform: platform,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
stdio: stdio,
|
stdio: stdio,
|
||||||
@ -454,29 +516,54 @@ void main() {
|
|||||||
|
|
||||||
test('with no engine cherrypicks but a dart revision update, updates engine revision', () async {
|
test('with no engine cherrypicks but a dart revision update, updates engine revision', () async {
|
||||||
stdio.stdin.add('n');
|
stdio.stdin.add('n');
|
||||||
processManager.addCommands(const <FakeCommand>[
|
processManager.addCommands(<FakeCommand>[
|
||||||
FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
// we want merged upstream commit, not local working commit
|
// we want merged upstream commit, not local working commit
|
||||||
FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
|
const FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision1,
|
stdout: revision1,
|
||||||
),
|
),
|
||||||
FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
FakeCommand(command: <String>['git', 'checkout', workingBranch]),
|
|
||||||
FakeCommand(
|
FakeCommand(
|
||||||
|
command: const <String>['git', 'checkout', workingBranch],
|
||||||
|
onRun: () {
|
||||||
|
final File file = fileSystem.file('$checkoutsParentDirectory/framework/.ci.yaml')
|
||||||
|
..createSync();
|
||||||
|
_initializeCiYamlFile(file);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision2,
|
stdout: revision2,
|
||||||
),
|
),
|
||||||
FakeCommand(command: <String>['git', 'add', '--all']),
|
const FakeCommand(
|
||||||
FakeCommand(command: <String>[
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM /path/to/.ci.yaml',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'commit',
|
||||||
|
"--message='add branch $candidateBranch to enabled_branches in .ci.yaml'",
|
||||||
|
]),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision3,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM /path/to/engine.version',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
'git',
|
'git',
|
||||||
'commit',
|
'commit',
|
||||||
"--message='Update Engine revision to $revision1 for $releaseChannel release $releaseVersion'",
|
"--message='Update Engine revision to $revision1 for $releaseChannel release $releaseVersion'",
|
||||||
]),
|
]),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision3,
|
stdout: revision4,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
final pb.ConductorState state = pb.ConductorState(
|
final pb.ConductorState state = pb.ConductorState(
|
||||||
@ -518,34 +605,59 @@ void main() {
|
|||||||
|
|
||||||
expect(processManager, hasNoRemainingExpectations);
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
expect(stdio.stdout, contains('Updating engine revision from $oldEngineVersion to $revision1'));
|
expect(stdio.stdout, contains('Updating engine revision from $oldEngineVersion to $revision1'));
|
||||||
expect(stdio.stdout, contains('a framework PR is still\nrequired to roll engine cherrypicks.'));
|
expect(stdio.stdout, contains('Are you ready to push your framework branch'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not update state.currentPhase if user responds no', () async {
|
test('does not update state.currentPhase if user responds no', () async {
|
||||||
stdio.stdin.add('n');
|
stdio.stdin.add('n');
|
||||||
processManager.addCommands(const <FakeCommand>[
|
processManager.addCommands(<FakeCommand>[
|
||||||
FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
// we want merged upstream commit, not local working commit
|
// we want merged upstream commit, not local working commit
|
||||||
FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
|
|
||||||
FakeCommand(
|
FakeCommand(
|
||||||
|
command: const <String>['git', 'checkout', 'upstream/$candidateBranch'],
|
||||||
|
onRun: () {
|
||||||
|
final File file = fileSystem.file('$checkoutsParentDirectory/framework/.ci.yaml')
|
||||||
|
..createSync();
|
||||||
|
_initializeCiYamlFile(file);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision1,
|
stdout: revision1,
|
||||||
),
|
),
|
||||||
FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
FakeCommand(command: <String>['git', 'checkout', workingBranch]),
|
const FakeCommand(command: <String>['git', 'checkout', workingBranch]),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision2,
|
stdout: revision2,
|
||||||
),
|
),
|
||||||
FakeCommand(command: <String>['git', 'add', '--all']),
|
const FakeCommand(
|
||||||
FakeCommand(command: <String>[
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/.ci.yaml',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'commit',
|
||||||
|
"--message='add branch $candidateBranch to enabled_branches in .ci.yaml'",
|
||||||
|
]),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision3,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/engine.version',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
'git',
|
'git',
|
||||||
'commit',
|
'commit',
|
||||||
"--message='Update Engine revision to $revision1 for $releaseChannel release $releaseVersion'",
|
"--message='Update Engine revision to $revision1 for $releaseChannel release $releaseVersion'",
|
||||||
]),
|
]),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision3,
|
stdout: revision4,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
writeStateToFile(
|
writeStateToFile(
|
||||||
@ -578,34 +690,59 @@ void main() {
|
|||||||
|
|
||||||
test('updates state.currentPhase if user responds yes', () async {
|
test('updates state.currentPhase if user responds yes', () async {
|
||||||
stdio.stdin.add('y');
|
stdio.stdin.add('y');
|
||||||
processManager.addCommands(const <FakeCommand>[
|
processManager.addCommands(<FakeCommand>[
|
||||||
// Engine repo
|
// Engine repo
|
||||||
FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
// we want merged upstream commit, not local working commit
|
// we want merged upstream commit, not local working commit
|
||||||
FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
|
const FakeCommand(command: <String>['git', 'checkout', 'upstream/$candidateBranch']),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision1,
|
stdout: revision1,
|
||||||
),
|
),
|
||||||
// Framework repo
|
// Framework repo
|
||||||
FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
const FakeCommand(command: <String>['git', 'fetch', 'upstream']),
|
||||||
FakeCommand(command: <String>['git', 'checkout', workingBranch]),
|
|
||||||
FakeCommand(
|
FakeCommand(
|
||||||
|
command: const <String>['git', 'checkout', workingBranch],
|
||||||
|
onRun: () {
|
||||||
|
final File file = fileSystem.file('$checkoutsParentDirectory/framework/.ci.yaml')
|
||||||
|
..createSync();
|
||||||
|
_initializeCiYamlFile(file);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision2,
|
stdout: revision2,
|
||||||
),
|
),
|
||||||
FakeCommand(command: <String>['git', 'add', '--all']),
|
const FakeCommand(
|
||||||
FakeCommand(command: <String>[
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/.ci.yaml',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'commit',
|
||||||
|
"--message='add branch $candidateBranch to enabled_branches in .ci.yaml'",
|
||||||
|
]),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision3,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/engine.version',
|
||||||
|
),
|
||||||
|
const FakeCommand(command: <String>['git', 'add', '--all']),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
'git',
|
'git',
|
||||||
'commit',
|
'commit',
|
||||||
"--message='Update Engine revision to $revision1 for $releaseChannel release $releaseVersion'",
|
"--message='Update Engine revision to $revision1 for $releaseChannel release $releaseVersion'",
|
||||||
]),
|
]),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'rev-parse', 'HEAD'],
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
stdout: revision3,
|
stdout: revision4,
|
||||||
),
|
),
|
||||||
FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'push', 'mirror', '$revision2:$workingBranch'],
|
command: <String>['git', 'push', 'mirror', '$revision2:refs/heads/$workingBranch'],
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
writeStateToFile(
|
writeStateToFile(
|
||||||
@ -646,7 +783,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
stdio.stdout,
|
stdio.stdout,
|
||||||
contains('Executed command: `git push mirror $revision2:$workingBranch`'),
|
contains('Executed command: `git push mirror $revision2:refs/heads/$workingBranch`'),
|
||||||
);
|
);
|
||||||
expect(stdio.error, isEmpty);
|
expect(stdio.error, isEmpty);
|
||||||
});
|
});
|
||||||
@ -941,3 +1078,34 @@ void main() {
|
|||||||
'windows': const Skip('Flutter Conductor only supported on macos/linux'),
|
'windows': const Skip('Flutter Conductor only supported on macos/linux'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _initializeCiYamlFile(
|
||||||
|
File file, {
|
||||||
|
List<String>? enabledBranches,
|
||||||
|
}) {
|
||||||
|
enabledBranches ??= <String>['master', 'dev', 'beta', 'stable'];
|
||||||
|
file.createSync(recursive: true);
|
||||||
|
final StringBuffer buffer = StringBuffer('enabled_branches:\n');
|
||||||
|
for (final String branch in enabledBranches) {
|
||||||
|
buffer.writeln(' - $branch');
|
||||||
|
}
|
||||||
|
buffer.writeln('''
|
||||||
|
|
||||||
|
platform_properties:
|
||||||
|
linux:
|
||||||
|
properties:
|
||||||
|
caches: ["name":"openjdk","path":"java"]
|
||||||
|
|
||||||
|
targets:
|
||||||
|
- name: Linux analyze
|
||||||
|
recipe: flutter/flutter
|
||||||
|
timeout: 60
|
||||||
|
properties:
|
||||||
|
tags: >
|
||||||
|
["framework","hostonly"]
|
||||||
|
validation: analyze
|
||||||
|
validation_name: Analyze
|
||||||
|
scheduler: luci
|
||||||
|
''');
|
||||||
|
file.writeAsStringSync(buffer.toString());
|
||||||
|
}
|
||||||
|
@ -224,7 +224,7 @@ vars = {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('commit() passes correct commit message', () {
|
test('commit() throws if there are no local changes to commit', () {
|
||||||
const String commit1 = 'abc123';
|
const String commit1 = 'abc123';
|
||||||
const String commit2 = 'def456';
|
const String commit2 = 'def456';
|
||||||
const String message = 'This is a commit message.';
|
const String message = 'This is a commit message.';
|
||||||
@ -251,6 +251,69 @@ vars = {
|
|||||||
'rev-parse',
|
'rev-parse',
|
||||||
'HEAD',
|
'HEAD',
|
||||||
], stdout: commit1),
|
], stdout: commit1),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'status',
|
||||||
|
'--porcelain',
|
||||||
|
]),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'commit',
|
||||||
|
"--message='$message'",
|
||||||
|
]),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'rev-parse',
|
||||||
|
'HEAD',
|
||||||
|
], stdout: commit2),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final Checkouts checkouts = Checkouts(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
parentDirectory: fileSystem.directory(rootDir),
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
stdio: stdio,
|
||||||
|
);
|
||||||
|
|
||||||
|
final EngineRepository repo = EngineRepository(checkouts);
|
||||||
|
expect(
|
||||||
|
() => repo.commit(message),
|
||||||
|
throwsExceptionWith('Tried to commit with message $message but no changes were present'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('commit() passes correct commit message', () {
|
||||||
|
const String commit1 = 'abc123';
|
||||||
|
const String commit2 = 'def456';
|
||||||
|
const String message = 'This is a commit message.';
|
||||||
|
final TestStdio stdio = TestStdio();
|
||||||
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--origin',
|
||||||
|
'upstream',
|
||||||
|
'--',
|
||||||
|
EngineRepository.defaultUpstream,
|
||||||
|
fileSystem.path
|
||||||
|
.join(rootDir, 'flutter_conductor_checkouts', 'engine'),
|
||||||
|
]),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'checkout',
|
||||||
|
'upstream/master',
|
||||||
|
]),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'rev-parse',
|
||||||
|
'HEAD',
|
||||||
|
], stdout: commit1),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/file.txt',
|
||||||
|
),
|
||||||
const FakeCommand(command: <String>[
|
const FakeCommand(command: <String>[
|
||||||
'git',
|
'git',
|
||||||
'commit',
|
'commit',
|
||||||
@ -274,6 +337,167 @@ vars = {
|
|||||||
final EngineRepository repo = EngineRepository(checkouts);
|
final EngineRepository repo = EngineRepository(checkouts);
|
||||||
repo.commit(message);
|
repo.commit(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('updateEngineRevision() returns false if newCommit is the same as version file', () {
|
||||||
|
const String commit1 = 'abc123';
|
||||||
|
const String commit2 = 'def456';
|
||||||
|
final TestStdio stdio = TestStdio();
|
||||||
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final File engineVersionFile = fileSystem.file('/engine.version')..writeAsStringSync(commit2);
|
||||||
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--origin',
|
||||||
|
'upstream',
|
||||||
|
'--',
|
||||||
|
FrameworkRepository.defaultUpstream,
|
||||||
|
fileSystem.path
|
||||||
|
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
|
||||||
|
]),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'rev-parse',
|
||||||
|
'HEAD',
|
||||||
|
], stdout: commit1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final Checkouts checkouts = Checkouts(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
parentDirectory: fileSystem.directory(rootDir),
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
stdio: stdio,
|
||||||
|
);
|
||||||
|
|
||||||
|
final FrameworkRepository repo = FrameworkRepository(checkouts);
|
||||||
|
final bool didUpdate = repo.updateEngineRevision(commit2, engineVersionFile: engineVersionFile);
|
||||||
|
expect(didUpdate, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CiYaml(file) will throw if file does not exist', () {
|
||||||
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final File file = fileSystem.file('/non/existent/file.txt');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => CiYaml(file),
|
||||||
|
throwsExceptionWith('Could not find the .ci.yaml file at /non/existent/file.txt'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ciYaml.enableBranch() will prepend the given branch to the yaml list of enabled_branches', () {
|
||||||
|
const String commit1 = 'abc123';
|
||||||
|
final TestStdio stdio = TestStdio();
|
||||||
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final File ciYaml = fileSystem.file('/flutter_conductor_checkouts/framework/.ci.yaml');
|
||||||
|
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--origin',
|
||||||
|
'upstream',
|
||||||
|
'--',
|
||||||
|
FrameworkRepository.defaultUpstream,
|
||||||
|
fileSystem.path
|
||||||
|
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
|
||||||
|
],
|
||||||
|
onRun: () {
|
||||||
|
ciYaml.createSync(recursive: true);
|
||||||
|
ciYaml.writeAsStringSync('''
|
||||||
|
enabled_branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
- beta
|
||||||
|
- stable
|
||||||
|
''');
|
||||||
|
}),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'rev-parse',
|
||||||
|
'HEAD',
|
||||||
|
], stdout: commit1),
|
||||||
|
]);
|
||||||
|
final Checkouts checkouts = Checkouts(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
parentDirectory: fileSystem.directory(rootDir),
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
stdio: stdio,
|
||||||
|
);
|
||||||
|
|
||||||
|
final FrameworkRepository framework = FrameworkRepository(checkouts);
|
||||||
|
expect(
|
||||||
|
framework.ciYaml.enabledBranches,
|
||||||
|
<String>['master', 'dev', 'beta', 'stable'],
|
||||||
|
);
|
||||||
|
|
||||||
|
framework.ciYaml.enableBranch('foo');
|
||||||
|
expect(
|
||||||
|
framework.ciYaml.enabledBranches,
|
||||||
|
<String>['foo', 'master', 'dev', 'beta', 'stable'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
framework.ciYaml.stringContents,
|
||||||
|
'''
|
||||||
|
enabled_branches:
|
||||||
|
- foo
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
- beta
|
||||||
|
- stable
|
||||||
|
'''
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ciYaml.enableBranch() will throw if the input branch is already present in the yaml file', () {
|
||||||
|
const String commit1 = 'abc123';
|
||||||
|
final TestStdio stdio = TestStdio();
|
||||||
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final File ciYaml = fileSystem.file('/flutter_conductor_checkouts/framework/.ci.yaml');
|
||||||
|
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--origin',
|
||||||
|
'upstream',
|
||||||
|
'--',
|
||||||
|
FrameworkRepository.defaultUpstream,
|
||||||
|
fileSystem.path
|
||||||
|
.join(rootDir, 'flutter_conductor_checkouts', 'framework'),
|
||||||
|
],
|
||||||
|
onRun: () {
|
||||||
|
ciYaml.createSync(recursive: true);
|
||||||
|
ciYaml.writeAsStringSync('''
|
||||||
|
enabled_branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
- beta
|
||||||
|
- stable
|
||||||
|
''');
|
||||||
|
}),
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'git',
|
||||||
|
'rev-parse',
|
||||||
|
'HEAD',
|
||||||
|
], stdout: commit1),
|
||||||
|
]);
|
||||||
|
final Checkouts checkouts = Checkouts(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
parentDirectory: fileSystem.directory(rootDir),
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
stdio: stdio,
|
||||||
|
);
|
||||||
|
|
||||||
|
final FrameworkRepository framework = FrameworkRepository(checkouts);
|
||||||
|
expect(
|
||||||
|
() => framework.ciYaml.enableBranch('master'),
|
||||||
|
throwsExceptionWith('.ci.yaml already contains the branch master'),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +161,10 @@ void main() {
|
|||||||
'cherrypicks-$candidateBranch',
|
'cherrypicks-$candidateBranch',
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/DEPS',
|
||||||
|
),
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>['git', 'add', '--all'],
|
command: <String>['git', 'add', '--all'],
|
||||||
),
|
),
|
||||||
@ -272,6 +276,7 @@ void main() {
|
|||||||
jsonDecode(stateFile.readAsStringSync()),
|
jsonDecode(stateFile.readAsStringSync()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
expect(state.isInitialized(), true);
|
expect(state.isInitialized(), true);
|
||||||
expect(state.releaseChannel, releaseChannel);
|
expect(state.releaseChannel, releaseChannel);
|
||||||
expect(state.releaseVersion, nextVersion);
|
expect(state.releaseVersion, nextVersion);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user