Unverified Commit f47eac9c authored by Alex's avatar Alex Committed by GitHub

changed subprocesses to async from sync (#89668)

* changed subprocess to async from sync
parent cd4b47ab
# codesign_integration_test takes longer than the default timeout which is 30s
# since it has to clone both the engine and framework repos, and that test is running
# asynchronously. The async function is being awaited more than 30s which counts as inactivity
# The default timeout needs to be extended to accomodate codesign_integration_test
timeout: 5m
...@@ -38,9 +38,9 @@ class CandidatesCommand extends Command<void> { ...@@ -38,9 +38,9 @@ class CandidatesCommand extends Command<void> {
String get description => 'List release candidates.'; String get description => 'List release candidates.';
@override @override
void run() { Future<void> run() async {
final ArgResults results = argResults!; final ArgResults results = argResults!;
git.run( await git.run(
<String>['fetch', results[kRemote] as String], <String>['fetch', results[kRemote] as String],
'Fetch from remote ${results[kRemote]}', 'Fetch from remote ${results[kRemote]}',
workingDirectory: flutterRoot.path, workingDirectory: flutterRoot.path,
...@@ -52,10 +52,10 @@ class CandidatesCommand extends Command<void> { ...@@ -52,10 +52,10 @@ class CandidatesCommand extends Command<void> {
upstreamPath: flutterRoot.path, upstreamPath: flutterRoot.path,
); );
final Version currentVersion = framework.flutterVersion(); final Version currentVersion = await framework.flutterVersion();
stdio.printStatus('currentVersion = $currentVersion'); stdio.printStatus('currentVersion = $currentVersion');
final List<String> branches = git.getOutput( final List<String> branches = (await git.getOutput(
<String>[ <String>[
'branch', 'branch',
'--no-color', '--no-color',
...@@ -65,7 +65,7 @@ class CandidatesCommand extends Command<void> { ...@@ -65,7 +65,7 @@ class CandidatesCommand extends Command<void> {
], ],
'List all remote branches', 'List all remote branches',
workingDirectory: flutterRoot.path, workingDirectory: flutterRoot.path,
).split('\n'); )).split('\n');
// Pattern for extracting only the branch name via sub-group 1 // Pattern for extracting only the branch name via sub-group 1
final RegExp remotePattern = RegExp('${results[kRemote]}\\/(.*)'); final RegExp remotePattern = RegExp('${results[kRemote]}\\/(.*)');
......
...@@ -89,7 +89,7 @@ class CodesignCommand extends Command<void> { ...@@ -89,7 +89,7 @@ class CodesignCommand extends Command<void> {
'For codesigning and verifying the signatures of engine binaries.'; 'For codesigning and verifying the signatures of engine binaries.';
@override @override
void run() { Future<void> run() async {
if (!platform.isMacOS) { if (!platform.isMacOS) {
throw ConductorException( throw ConductorException(
'Error! Expected operating system "macos", actual operating system is: ' 'Error! Expected operating system "macos", actual operating system is: '
...@@ -112,21 +112,21 @@ class CodesignCommand extends Command<void> { ...@@ -112,21 +112,21 @@ class CodesignCommand extends Command<void> {
'the desired revision and run that version of the conductor.\n'); 'the desired revision and run that version of the conductor.\n');
revision = argResults![kRevision] as String; revision = argResults![kRevision] as String;
} else { } else {
revision = (processManager.runSync( revision = ((await processManager.run(
<String>['git', 'rev-parse', 'HEAD'], <String>['git', 'rev-parse', 'HEAD'],
workingDirectory: framework.checkoutDirectory.path, workingDirectory: (await framework.checkoutDirectory).path,
).stdout as String).trim(); )).stdout as String).trim();
assert(revision.isNotEmpty); assert(revision.isNotEmpty);
} }
framework.checkout(revision); await framework.checkout(revision);
// Ensure artifacts present // Ensure artifacts present
framework.runFlutter(<String>['precache', '--android', '--ios', '--macos']); await framework.runFlutter(<String>['precache', '--android', '--ios', '--macos']);
verifyExist(); await verifyExist();
if (argResults![kSignatures] as bool) { if (argResults![kSignatures] as bool) {
verifySignatures(); await verifySignatures();
} }
} }
...@@ -134,7 +134,8 @@ class CodesignCommand extends Command<void> { ...@@ -134,7 +134,8 @@ class CodesignCommand extends Command<void> {
/// ///
/// This list should be kept in sync with the actual contents of Flutter's /// This list should be kept in sync with the actual contents of Flutter's
/// cache. /// cache.
List<String> get binariesWithEntitlements { Future<List<String>> get binariesWithEntitlements async {
final String frameworkCacheDirectory = await framework.cacheDirectory;
return <String>[ return <String>[
'artifacts/engine/android-arm-profile/darwin-x64/gen_snapshot', 'artifacts/engine/android-arm-profile/darwin-x64/gen_snapshot',
'artifacts/engine/android-arm-release/darwin-x64/gen_snapshot', 'artifacts/engine/android-arm-release/darwin-x64/gen_snapshot',
...@@ -165,7 +166,7 @@ class CodesignCommand extends Command<void> { ...@@ -165,7 +166,7 @@ class CodesignCommand extends Command<void> {
'dart-sdk/bin/utils/gen_snapshot', 'dart-sdk/bin/utils/gen_snapshot',
] ]
.map((String relativePath) => .map((String relativePath) =>
fileSystem.path.join(framework.cacheDirectory, relativePath)) fileSystem.path.join(frameworkCacheDirectory, relativePath))
.toList(); .toList();
} }
...@@ -173,7 +174,8 @@ class CodesignCommand extends Command<void> { ...@@ -173,7 +174,8 @@ class CodesignCommand extends Command<void> {
/// ///
/// This list should be kept in sync with the actual contents of Flutter's /// This list should be kept in sync with the actual contents of Flutter's
/// cache. /// cache.
List<String> get binariesWithoutEntitlements { Future<List<String>> get binariesWithoutEntitlements async {
final String frameworkCacheDirectory = await framework.cacheDirectory;
return <String>[ return <String>[
'artifacts/engine/darwin-x64-profile/FlutterMacOS.framework/Versions/A/FlutterMacOS', 'artifacts/engine/darwin-x64-profile/FlutterMacOS.framework/Versions/A/FlutterMacOS',
'artifacts/engine/darwin-x64-release/FlutterMacOS.framework/Versions/A/FlutterMacOS', 'artifacts/engine/darwin-x64-release/FlutterMacOS.framework/Versions/A/FlutterMacOS',
...@@ -188,7 +190,7 @@ class CodesignCommand extends Command<void> { ...@@ -188,7 +190,7 @@ class CodesignCommand extends Command<void> {
'artifacts/ios-deploy/ios-deploy', 'artifacts/ios-deploy/ios-deploy',
] ]
.map((String relativePath) => .map((String relativePath) =>
fileSystem.path.join(framework.cacheDirectory, relativePath)) fileSystem.path.join(frameworkCacheDirectory, relativePath))
.toList(); .toList();
} }
...@@ -200,12 +202,13 @@ class CodesignCommand extends Command<void> { ...@@ -200,12 +202,13 @@ class CodesignCommand extends Command<void> {
/// [binariesWithEntitlements] or [binariesWithoutEntitlements] lists should /// [binariesWithEntitlements] or [binariesWithoutEntitlements] lists should
/// be updated accordingly. /// be updated accordingly.
@visibleForTesting @visibleForTesting
void verifyExist() { Future<void> verifyExist() async {
final Set<String> foundFiles = <String>{}; final Set<String> foundFiles = <String>{};
for (final String binaryPath in findBinaryPaths(framework.cacheDirectory)) { for (final String binaryPath
if (binariesWithEntitlements.contains(binaryPath)) { in await findBinaryPaths(await framework.cacheDirectory)) {
if ((await binariesWithEntitlements).contains(binaryPath)) {
foundFiles.add(binaryPath); foundFiles.add(binaryPath);
} else if (binariesWithoutEntitlements.contains(binaryPath)) { } else if ((await binariesWithoutEntitlements).contains(binaryPath)) {
foundFiles.add(binaryPath); foundFiles.add(binaryPath);
} else { } else {
throw ConductorException( throw ConductorException(
...@@ -214,7 +217,7 @@ class CodesignCommand extends Command<void> { ...@@ -214,7 +217,7 @@ class CodesignCommand extends Command<void> {
} }
final List<String> allExpectedFiles = final List<String> allExpectedFiles =
binariesWithEntitlements + binariesWithoutEntitlements; (await binariesWithEntitlements) + (await binariesWithoutEntitlements);
if (foundFiles.length < allExpectedFiles.length) { if (foundFiles.length < allExpectedFiles.length) {
final List<String> unfoundFiles = allExpectedFiles final List<String> unfoundFiles = allExpectedFiles
.where( .where(
...@@ -237,19 +240,19 @@ class CodesignCommand extends Command<void> { ...@@ -237,19 +240,19 @@ class CodesignCommand extends Command<void> {
/// Verify code signatures and entitlements of all binaries in the cache. /// Verify code signatures and entitlements of all binaries in the cache.
@visibleForTesting @visibleForTesting
void verifySignatures() { Future<void> verifySignatures() async {
final List<String> unsignedBinaries = <String>[]; final List<String> unsignedBinaries = <String>[];
final List<String> wrongEntitlementBinaries = <String>[]; final List<String> wrongEntitlementBinaries = <String>[];
final List<String> unexpectedBinaries = <String>[]; final List<String> unexpectedBinaries = <String>[];
for (final String binaryPath
for (final String binaryPath in findBinaryPaths(framework.cacheDirectory)) { in await findBinaryPaths(await framework.cacheDirectory)) {
bool verifySignature = false; bool verifySignature = false;
bool verifyEntitlements = false; bool verifyEntitlements = false;
if (binariesWithEntitlements.contains(binaryPath)) { if ((await binariesWithEntitlements).contains(binaryPath)) {
verifySignature = true; verifySignature = true;
verifyEntitlements = true; verifyEntitlements = true;
} }
if (binariesWithoutEntitlements.contains(binaryPath)) { if ((await binariesWithoutEntitlements).contains(binaryPath)) {
verifySignature = true; verifySignature = true;
} }
if (!verifySignature && !verifyEntitlements) { if (!verifySignature && !verifyEntitlements) {
...@@ -258,7 +261,7 @@ class CodesignCommand extends Command<void> { ...@@ -258,7 +261,7 @@ class CodesignCommand extends Command<void> {
continue; continue;
} }
stdio.printTrace('Verifying the code signature of $binaryPath'); stdio.printTrace('Verifying the code signature of $binaryPath');
final io.ProcessResult codeSignResult = processManager.runSync( final io.ProcessResult codeSignResult = await processManager.run(
<String>[ <String>[
'codesign', 'codesign',
'-vvv', '-vvv',
...@@ -275,7 +278,7 @@ class CodesignCommand extends Command<void> { ...@@ -275,7 +278,7 @@ class CodesignCommand extends Command<void> {
} }
if (verifyEntitlements) { if (verifyEntitlements) {
stdio.printTrace('Verifying entitlements of $binaryPath'); stdio.printTrace('Verifying entitlements of $binaryPath');
if (!hasExpectedEntitlements(binaryPath)) { if (!(await hasExpectedEntitlements(binaryPath))) {
wrongEntitlementBinaries.add(binaryPath); wrongEntitlementBinaries.add(binaryPath);
} }
} }
...@@ -330,11 +333,12 @@ class CodesignCommand extends Command<void> { ...@@ -330,11 +333,12 @@ class CodesignCommand extends Command<void> {
List<String>? _allBinaryPaths; List<String>? _allBinaryPaths;
/// Find every binary file in the given [rootDirectory]. /// Find every binary file in the given [rootDirectory].
List<String> findBinaryPaths(String rootDirectory) { Future<List<String>> findBinaryPaths(String rootDirectory) async {
if (_allBinaryPaths != null) { if (_allBinaryPaths != null) {
return _allBinaryPaths!; return _allBinaryPaths!;
} }
final io.ProcessResult result = processManager.runSync( final List<String> allBinaryPaths = <String>[];
final io.ProcessResult result = await processManager.run(
<String>[ <String>[
'find', 'find',
rootDirectory, rootDirectory,
...@@ -346,13 +350,19 @@ class CodesignCommand extends Command<void> { ...@@ -346,13 +350,19 @@ class CodesignCommand extends Command<void> {
.split('\n') .split('\n')
.where((String s) => s.isNotEmpty) .where((String s) => s.isNotEmpty)
.toList(); .toList();
_allBinaryPaths = allFiles.where(isBinary).toList();
await Future.forEach(allFiles, (String filePath) async {
if (await isBinary(filePath)) {
allBinaryPaths.add(filePath);
}
});
_allBinaryPaths = allBinaryPaths;
return _allBinaryPaths!; return _allBinaryPaths!;
} }
/// Check mime-type of file at [filePath] to determine if it is binary. /// Check mime-type of file at [filePath] to determine if it is binary.
bool isBinary(String filePath) { Future<bool> isBinary(String filePath) async {
final io.ProcessResult result = processManager.runSync( final io.ProcessResult result = await processManager.run(
<String>[ <String>[
'file', 'file',
'--mime-type', '--mime-type',
...@@ -364,8 +374,8 @@ class CodesignCommand extends Command<void> { ...@@ -364,8 +374,8 @@ class CodesignCommand extends Command<void> {
} }
/// Check if the binary has the expected entitlements. /// Check if the binary has the expected entitlements.
bool hasExpectedEntitlements(String binaryPath) { Future<bool> hasExpectedEntitlements(String binaryPath) async {
final io.ProcessResult entitlementResult = processManager.runSync( final io.ProcessResult entitlementResult = await processManager.run(
<String>[ <String>[
'codesign', 'codesign',
'--display', '--display',
...@@ -386,7 +396,7 @@ class CodesignCommand extends Command<void> { ...@@ -386,7 +396,7 @@ class CodesignCommand extends Command<void> {
final String output = entitlementResult.stdout as String; final String output = entitlementResult.stdout as String;
for (final String entitlement in expectedEntitlements) { for (final String entitlement in expectedEntitlements) {
final bool entitlementExpected = final bool entitlementExpected =
binariesWithEntitlements.contains(binaryPath); (await binariesWithEntitlements).contains(binaryPath);
if (output.contains(entitlement) != entitlementExpected) { if (output.contains(entitlement) != entitlementExpected) {
stdio.printError( stdio.printError(
'File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} ' 'File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} '
......
...@@ -14,34 +14,34 @@ class Git { ...@@ -14,34 +14,34 @@ class Git {
final ProcessManager processManager; final ProcessManager processManager;
String getOutput( Future<String> getOutput(
List<String> args, List<String> args,
String explanation, { String explanation, {
required String workingDirectory, required String workingDirectory,
bool allowFailures = false, bool allowFailures = false,
}) { }) async {
final ProcessResult result = _run(args, workingDirectory); final ProcessResult result = await _run(args, workingDirectory);
if (result.exitCode == 0) { if (result.exitCode == 0) {
return stdoutToString(result.stdout); return stdoutToString(result.stdout);
} }
_reportFailureAndExit(args, workingDirectory, result, explanation); _reportFailureAndExit(args, workingDirectory, result, explanation);
} }
int run( Future<int> run(
List<String> args, List<String> args,
String explanation, { String explanation, {
bool allowNonZeroExitCode = false, bool allowNonZeroExitCode = false,
required String workingDirectory, required String workingDirectory,
}) { }) async {
final ProcessResult result = _run(args, workingDirectory); final ProcessResult result = await _run(args, workingDirectory);
if (result.exitCode != 0 && !allowNonZeroExitCode) { if (result.exitCode != 0 && !allowNonZeroExitCode) {
_reportFailureAndExit(args, workingDirectory, result, explanation); _reportFailureAndExit(args, workingDirectory, result, explanation);
} }
return result.exitCode; return result.exitCode;
} }
ProcessResult _run(List<String> args, String workingDirectory) { Future<ProcessResult> _run(List<String> args, String workingDirectory) async {
return processManager.runSync( return processManager.run(
<String>['git', ...args], <String>['git', ...args],
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
environment: <String, String>{'GIT_TRACE': '1'}, environment: <String, String>{'GIT_TRACE': '1'},
......
...@@ -47,8 +47,8 @@ class NextCommand extends Command<void> { ...@@ -47,8 +47,8 @@ class NextCommand extends Command<void> {
String get description => 'Proceed to the next release phase.'; String get description => 'Proceed to the next release phase.';
@override @override
void run() { Future<void> run() async {
runNext( await runNext(
autoAccept: argResults![kYesFlag] as bool, autoAccept: argResults![kYesFlag] as bool,
checkouts: checkouts, checkouts: checkouts,
force: argResults![kForceFlag] as bool, force: argResults![kForceFlag] as bool,
...@@ -74,12 +74,12 @@ bool prompt(String message, Stdio stdio) { ...@@ -74,12 +74,12 @@ bool prompt(String message, Stdio stdio) {
} }
@visibleForTesting @visibleForTesting
void runNext({ Future<void> runNext({
required bool autoAccept, required bool autoAccept,
required bool force, required bool force,
required Checkouts checkouts, required Checkouts checkouts,
required File stateFile, required File stateFile,
}) { }) async {
final Stdio stdio = checkouts.stdio; final Stdio stdio = checkouts.stdio;
const List<CherrypickState> finishedStates = <CherrypickState>[ const List<CherrypickState> finishedStates = <CherrypickState>[
CherrypickState.COMPLETED, CherrypickState.COMPLETED,
...@@ -106,10 +106,11 @@ void runNext({ ...@@ -106,10 +106,11 @@ void runNext({
previousCheckoutLocation: state.engine.checkoutPath, previousCheckoutLocation: state.engine.checkoutPath,
); );
// check if the candidate branch is enabled in .ci.yaml // check if the candidate branch is enabled in .ci.yaml
if (!engine.ciYaml.enabledBranches.contains(state.engine.candidateBranch)) { final CiYaml engineCiYaml = await engine.ciYaml;
engine.ciYaml.enableBranch(state.engine.candidateBranch); if (!engineCiYaml.enabledBranches.contains(state.engine.candidateBranch)) {
engineCiYaml.enableBranch(state.engine.candidateBranch);
// commit // commit
final String revision = engine.commit( final String revision = await engine.commit(
'add branch ${state.engine.candidateBranch} to enabled_branches in .ci.yaml', 'add branch ${state.engine.candidateBranch} to enabled_branches in .ci.yaml',
addFirst: true, addFirst: true,
); );
...@@ -158,7 +159,7 @@ void runNext({ ...@@ -158,7 +159,7 @@ void runNext({
} }
} }
engine.pushRef( await engine.pushRef(
fromRef: 'HEAD', fromRef: 'HEAD',
// Explicitly create new branch // Explicitly create new branch
toRef: 'refs/heads/${state.engine.workingBranch}', toRef: 'refs/heads/${state.engine.workingBranch}',
...@@ -211,7 +212,7 @@ void runNext({ ...@@ -211,7 +212,7 @@ void runNext({
previousCheckoutLocation: state.engine.checkoutPath, previousCheckoutLocation: state.engine.checkoutPath,
); );
final String engineRevision = engine.reverseParse('HEAD'); final String engineRevision = await engine.reverseParse('HEAD');
final Remote upstream = Remote( final Remote upstream = Remote(
name: RemoteName.upstream, name: RemoteName.upstream,
...@@ -225,10 +226,10 @@ void runNext({ ...@@ -225,10 +226,10 @@ void runNext({
); );
// Check if the current candidate branch is enabled // Check if the current candidate branch is enabled
if (!framework.ciYaml.enabledBranches.contains(state.framework.candidateBranch)) { if (!(await framework.ciYaml).enabledBranches.contains(state.framework.candidateBranch)) {
framework.ciYaml.enableBranch(state.framework.candidateBranch); (await framework.ciYaml).enableBranch(state.framework.candidateBranch);
// commit // commit
final String revision = framework.commit( final String revision = await framework.commit(
'add branch ${state.framework.candidateBranch} to enabled_branches in .ci.yaml', 'add branch ${state.framework.candidateBranch} to enabled_branches in .ci.yaml',
addFirst: true, addFirst: true,
); );
...@@ -240,9 +241,9 @@ void runNext({ ...@@ -240,9 +241,9 @@ void runNext({
} }
stdio.printStatus('Rolling new engine hash $engineRevision to framework checkout...'); stdio.printStatus('Rolling new engine hash $engineRevision to framework checkout...');
final bool needsCommit = framework.updateEngineRevision(engineRevision); final bool needsCommit = await framework.updateEngineRevision(engineRevision);
if (needsCommit) { if (needsCommit) {
final String revision = framework.commit( final String revision = await 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,
); );
...@@ -293,7 +294,7 @@ void runNext({ ...@@ -293,7 +294,7 @@ void runNext({
} }
} }
framework.pushRef( await framework.pushRef(
fromRef: 'HEAD', fromRef: 'HEAD',
// Explicitly create new branch // Explicitly create new branch
toRef: 'refs/heads/${state.framework.workingBranch}', toRef: 'refs/heads/${state.framework.workingBranch}',
...@@ -314,7 +315,7 @@ void runNext({ ...@@ -314,7 +315,7 @@ void runNext({
upstreamRemote: upstream, upstreamRemote: upstream,
previousCheckoutLocation: state.framework.checkoutPath, previousCheckoutLocation: state.framework.checkoutPath,
); );
final String headRevision = framework.reverseParse('HEAD'); final String headRevision = await framework.reverseParse('HEAD');
if (autoAccept == false) { if (autoAccept == false) {
final bool response = prompt( final bool response = prompt(
'Are you ready to tag commit $headRevision as ${state.releaseVersion}\n' 'Are you ready to tag commit $headRevision as ${state.releaseVersion}\n'
...@@ -327,7 +328,7 @@ void runNext({ ...@@ -327,7 +328,7 @@ void runNext({
return; return;
} }
} }
framework.tag(headRevision, state.releaseVersion, upstream.name); await framework.tag(headRevision, state.releaseVersion, upstream.name);
break; break;
case pb.ReleasePhase.PUBLISH_CHANNEL: case pb.ReleasePhase.PUBLISH_CHANNEL:
final Remote upstream = Remote( final Remote upstream = Remote(
...@@ -341,10 +342,10 @@ void runNext({ ...@@ -341,10 +342,10 @@ void runNext({
upstreamRemote: upstream, upstreamRemote: upstream,
previousCheckoutLocation: state.framework.checkoutPath, previousCheckoutLocation: state.framework.checkoutPath,
); );
final String headRevision = framework.reverseParse('HEAD'); final String headRevision = await framework.reverseParse('HEAD');
if (autoAccept == false) { if (autoAccept == false) {
// dryRun: true means print out git command // dryRun: true means print out git command
framework.pushRef( await framework.pushRef(
fromRef: headRevision, fromRef: headRevision,
toRef: state.releaseChannel, toRef: state.releaseChannel,
remote: state.framework.upstream.url, remote: state.framework.upstream.url,
...@@ -362,7 +363,7 @@ void runNext({ ...@@ -362,7 +363,7 @@ void runNext({
return; return;
} }
} }
framework.pushRef( await framework.pushRef(
fromRef: headRevision, fromRef: headRevision,
toRef: state.releaseChannel, toRef: state.releaseChannel,
remote: state.framework.upstream.url, remote: state.framework.upstream.url,
......
...@@ -58,34 +58,11 @@ abstract class Repository { ...@@ -58,34 +58,11 @@ abstract class Repository {
required this.parentDirectory, required this.parentDirectory,
this.initialRef, this.initialRef,
this.localUpstream = false, this.localUpstream = false,
String? previousCheckoutLocation, this.previousCheckoutLocation,
this.mirrorRemote, this.mirrorRemote,
}) : git = Git(processManager), }) : git = Git(processManager),
assert(localUpstream != null), assert(localUpstream != null),
assert(upstreamRemote.url.isNotEmpty) { assert(upstreamRemote.url.isNotEmpty);
if (previousCheckoutLocation != null) {
_checkoutDirectory = fileSystem.directory(previousCheckoutLocation);
if (!_checkoutDirectory!.existsSync()) {
throw ConductorException(
'Provided previousCheckoutLocation $previousCheckoutLocation does not exist on disk!');
}
if (initialRef != null) {
assert(initialRef != '');
git.run(
<String>['fetch', upstreamRemote.name],
'Fetch ${upstreamRemote.name} to ensure we have latest refs',
workingDirectory: _checkoutDirectory!.path,
);
// Note: if [initialRef] is a remote ref the checkout will be left in a
// detached HEAD state.
git.run(
<String>['checkout', initialRef!],
'Checking out initialRef $initialRef',
workingDirectory: _checkoutDirectory!.path,
);
}
}
}
final String name; final String name;
final Remote upstreamRemote; final Remote upstreamRemote;
...@@ -109,22 +86,47 @@ abstract class Repository { ...@@ -109,22 +86,47 @@ abstract class Repository {
final bool localUpstream; final bool localUpstream;
Directory? _checkoutDirectory; Directory? _checkoutDirectory;
String? previousCheckoutLocation;
/// Directory for the repository checkout. /// Directory for the repository checkout.
/// ///
/// Since cloning a repository takes a long time, we do not ensure it is /// Since cloning a repository takes a long time, we do not ensure it is
/// cloned on the filesystem until this getter is accessed. /// cloned on the filesystem until this getter is accessed.
Directory get checkoutDirectory { Future<Directory> get checkoutDirectory async {
if (_checkoutDirectory != null) { if (_checkoutDirectory != null) {
return _checkoutDirectory!; return _checkoutDirectory!;
} }
if (previousCheckoutLocation != null) {
_checkoutDirectory = fileSystem.directory(previousCheckoutLocation);
if (!_checkoutDirectory!.existsSync()) {
throw ConductorException(
'Provided previousCheckoutLocation $previousCheckoutLocation does not exist on disk!');
}
if (initialRef != null) {
assert(initialRef != '');
await git.run(
<String>['fetch', upstreamRemote.name],
'Fetch ${upstreamRemote.name} to ensure we have latest refs',
workingDirectory: _checkoutDirectory!.path,
);
// Note: if [initialRef] is a remote ref the checkout will be left in a
// detached HEAD state.
await git.run(
<String>['checkout', initialRef!],
'Checking out initialRef $initialRef',
workingDirectory: _checkoutDirectory!.path,
);
}
return _checkoutDirectory!;
}
_checkoutDirectory = parentDirectory.childDirectory(name); _checkoutDirectory = parentDirectory.childDirectory(name);
lazilyInitialize(_checkoutDirectory!); await lazilyInitialize(_checkoutDirectory!);
return _checkoutDirectory!; return _checkoutDirectory!;
} }
/// Ensure the repository is cloned to disk and initialized with proper state. /// Ensure the repository is cloned to disk and initialized with proper state.
void lazilyInitialize(Directory checkoutDirectory) { Future<void> lazilyInitialize(Directory checkoutDirectory) async {
if (checkoutDirectory.existsSync()) { if (checkoutDirectory.existsSync()) {
stdio.printTrace('Deleting $name from ${checkoutDirectory.path}...'); stdio.printTrace('Deleting $name from ${checkoutDirectory.path}...');
checkoutDirectory.deleteSync(recursive: true); checkoutDirectory.deleteSync(recursive: true);
...@@ -133,7 +135,7 @@ abstract class Repository { ...@@ -133,7 +135,7 @@ abstract class Repository {
stdio.printTrace( stdio.printTrace(
'Cloning $name from ${upstreamRemote.url} to ${checkoutDirectory.path}...', 'Cloning $name from ${upstreamRemote.url} to ${checkoutDirectory.path}...',
); );
git.run( await git.run(
<String>[ <String>[
'clone', 'clone',
'--origin', '--origin',
...@@ -146,12 +148,12 @@ abstract class Repository { ...@@ -146,12 +148,12 @@ abstract class Repository {
workingDirectory: parentDirectory.path, workingDirectory: parentDirectory.path,
); );
if (mirrorRemote != null) { if (mirrorRemote != null) {
git.run( await git.run(
<String>['remote', 'add', mirrorRemote!.name, mirrorRemote!.url], <String>['remote', 'add', mirrorRemote!.name, mirrorRemote!.url],
'Adding remote ${mirrorRemote!.url} as ${mirrorRemote!.name}', 'Adding remote ${mirrorRemote!.url} as ${mirrorRemote!.name}',
workingDirectory: checkoutDirectory.path, workingDirectory: checkoutDirectory.path,
); );
git.run( await git.run(
<String>['fetch', mirrorRemote!.name], <String>['fetch', mirrorRemote!.name],
'Fetching git remote ${mirrorRemote!.name}', 'Fetching git remote ${mirrorRemote!.name}',
workingDirectory: checkoutDirectory.path, workingDirectory: checkoutDirectory.path,
...@@ -161,7 +163,7 @@ abstract class Repository { ...@@ -161,7 +163,7 @@ abstract class Repository {
// These branches must exist locally for the repo that depends on it // These branches must exist locally for the repo that depends on it
// to fetch and push to. // to fetch and push to.
for (final String channel in kReleaseChannels) { for (final String channel in kReleaseChannels) {
git.run( await git.run(
<String>['checkout', channel, '--'], <String>['checkout', channel, '--'],
'check out branch $channel locally', 'check out branch $channel locally',
workingDirectory: checkoutDirectory.path, workingDirectory: checkoutDirectory.path,
...@@ -170,82 +172,82 @@ abstract class Repository { ...@@ -170,82 +172,82 @@ abstract class Repository {
} }
if (initialRef != null) { if (initialRef != null) {
git.run( await git.run(
<String>['checkout', '${upstreamRemote.name}/$initialRef'], <String>['checkout', '${upstreamRemote.name}/$initialRef'],
'Checking out initialRef $initialRef', 'Checking out initialRef $initialRef',
workingDirectory: checkoutDirectory.path, workingDirectory: checkoutDirectory.path,
); );
} }
final String revision = reverseParse('HEAD'); final String revision = await reverseParse('HEAD');
stdio.printTrace( stdio.printTrace(
'Repository $name is checked out at revision "$revision".', 'Repository $name is checked out at revision "$revision".',
); );
} }
/// The URL of the remote named [remoteName]. /// The URL of the remote named [remoteName].
String remoteUrl(String remoteName) { Future<String> remoteUrl(String remoteName) async {
assert(remoteName != null); assert(remoteName != null);
return git.getOutput( return git.getOutput(
<String>['remote', 'get-url', remoteName], <String>['remote', 'get-url', remoteName],
'verify the URL of the $remoteName remote', 'verify the URL of the $remoteName remote',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// Verify the repository's git checkout is clean. /// Verify the repository's git checkout is clean.
bool gitCheckoutClean() { Future<bool> gitCheckoutClean() async {
final String output = git.getOutput( final String output = await git.getOutput(
<String>['status', '--porcelain'], <String>['status', '--porcelain'],
'check that the git checkout is clean', 'check that the git checkout is clean',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
return output == ''; return output == '';
} }
/// Return the revision for the branch point between two refs. /// Return the revision for the branch point between two refs.
String branchPoint(String firstRef, String secondRef) { Future<String> branchPoint(String firstRef, String secondRef) async {
return git.getOutput( return (await git.getOutput(
<String>['merge-base', firstRef, secondRef], <String>['merge-base', firstRef, secondRef],
'determine the merge base between $firstRef and $secondRef', 'determine the merge base between $firstRef and $secondRef',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
).trim(); )).trim();
} }
/// Fetch all branches and associated commits and tags from [remoteName]. /// Fetch all branches and associated commits and tags from [remoteName].
void fetch(String remoteName) { Future<void> fetch(String remoteName) async {
git.run( await git.run(
<String>['fetch', remoteName, '--tags'], <String>['fetch', remoteName, '--tags'],
'fetch $remoteName --tags', 'fetch $remoteName --tags',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// Create (and checkout) a new branch based on the current HEAD. /// Create (and checkout) a new branch based on the current HEAD.
/// ///
/// Runs `git checkout -b $branchName`. /// Runs `git checkout -b $branchName`.
void newBranch(String branchName) { Future<void> newBranch(String branchName) async {
git.run( await git.run(
<String>['checkout', '-b', branchName], <String>['checkout', '-b', branchName],
'create & checkout new branch $branchName', 'create & checkout new branch $branchName',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// Check out the given ref. /// Check out the given ref.
void checkout(String ref) { Future<void> checkout(String ref) async {
git.run( await git.run(
<String>['checkout', ref], <String>['checkout', ref],
'checkout ref', 'checkout ref',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// Obtain the version tag at the tip of a release branch. /// Obtain the version tag at the tip of a release branch.
String getFullTag( Future<String> getFullTag(
String remoteName, String remoteName,
String branchName, { String branchName, {
bool exact = true, bool exact = true,
}) { }) async {
// includes both stable (e.g. 1.2.3) and dev tags (e.g. 1.2.3-4.5.pre) // includes both stable (e.g. 1.2.3) and dev tags (e.g. 1.2.3-4.5.pre)
const String glob = '*.*.*'; const String glob = '*.*.*';
// describe the latest dev release // describe the latest dev release
...@@ -260,34 +262,33 @@ abstract class Repository { ...@@ -260,34 +262,33 @@ abstract class Repository {
ref, ref,
], ],
'obtain last released version number', 'obtain last released version number',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// List commits in reverse chronological order. /// List commits in reverse chronological order.
List<String> revList(List<String> args) { Future<List<String>> revList(List<String> args) async {
return git return (await git.getOutput(<String>['rev-list', ...args],
.getOutput(<String>['rev-list', ...args],
'rev-list with args ${args.join(' ')}', 'rev-list with args ${args.join(' ')}',
workingDirectory: checkoutDirectory.path) workingDirectory: (await checkoutDirectory).path))
.trim() .trim()
.split('\n'); .split('\n');
} }
/// Look up the commit for [ref]. /// Look up the commit for [ref].
String reverseParse(String ref) { Future<String> reverseParse(String ref) async {
final String revisionHash = git.getOutput( final String revisionHash = await git.getOutput(
<String>['rev-parse', ref], <String>['rev-parse', ref],
'look up the commit for the ref $ref', 'look up the commit for the ref $ref',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
assert(revisionHash.isNotEmpty); assert(revisionHash.isNotEmpty);
return revisionHash; return revisionHash;
} }
/// Determines if one ref is an ancestor for another. /// Determines if one ref is an ancestor for another.
bool isAncestor(String possibleAncestor, String possibleDescendant) { Future<bool> isAncestor(String possibleAncestor, String possibleDescendant) async {
final int exitcode = git.run( final int exitcode = await git.run(
<String>[ <String>[
'merge-base', 'merge-base',
'--is-ancestor', '--is-ancestor',
...@@ -296,83 +297,83 @@ abstract class Repository { ...@@ -296,83 +297,83 @@ abstract class Repository {
], ],
'verify $possibleAncestor is a direct ancestor of $possibleDescendant.', 'verify $possibleAncestor is a direct ancestor of $possibleDescendant.',
allowNonZeroExitCode: true, allowNonZeroExitCode: true,
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
return exitcode == 0; return exitcode == 0;
} }
/// Determines if a given commit has a tag. /// Determines if a given commit has a tag.
bool isCommitTagged(String commit) { Future<bool> isCommitTagged(String commit) async {
final int exitcode = git.run( final int exitcode = await git.run(
<String>['describe', '--exact-match', '--tags', commit], <String>['describe', '--exact-match', '--tags', commit],
'verify $commit is already tagged', 'verify $commit is already tagged',
allowNonZeroExitCode: true, allowNonZeroExitCode: true,
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
return exitcode == 0; return exitcode == 0;
} }
/// Determines if a commit will cherry-pick to current HEAD without conflict. /// Determines if a commit will cherry-pick to current HEAD without conflict.
bool canCherryPick(String commit) { Future<bool> canCherryPick(String commit) async {
assert( assert(
gitCheckoutClean(), await gitCheckoutClean(),
'cannot cherry-pick because git checkout ${checkoutDirectory.path} is not clean', 'cannot cherry-pick because git checkout ${(await checkoutDirectory).path} is not clean',
); );
final int exitcode = git.run( final int exitcode = await git.run(
<String>['cherry-pick', '--no-commit', commit], <String>['cherry-pick', '--no-commit', commit],
'attempt to cherry-pick $commit without committing', 'attempt to cherry-pick $commit without committing',
allowNonZeroExitCode: true, allowNonZeroExitCode: true,
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
final bool result = exitcode == 0; final bool result = exitcode == 0;
if (result == false) { if (result == false) {
stdio.printError(git.getOutput( stdio.printError(await git.getOutput(
<String>['diff'], <String>['diff'],
'get diff of failed cherry-pick', 'get diff of failed cherry-pick',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
)); ));
} }
reset('HEAD'); await reset('HEAD');
return result; return result;
} }
/// Cherry-pick a [commit] to the current HEAD. /// Cherry-pick a [commit] to the current HEAD.
/// ///
/// This method will throw a [GitException] if the command fails. /// This method will throw a [GitException] if the command fails.
void cherryPick(String commit) { Future<void> cherryPick(String commit) async {
assert( assert(
gitCheckoutClean(), await gitCheckoutClean(),
'cannot cherry-pick because git checkout ${checkoutDirectory.path} is not clean', 'cannot cherry-pick because git checkout ${(await checkoutDirectory).path} is not clean',
); );
git.run( await git.run(
<String>['cherry-pick', commit], <String>['cherry-pick', commit],
'cherry-pick $commit', 'cherry-pick $commit',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// Resets repository HEAD to [ref]. /// Resets repository HEAD to [ref].
void reset(String ref) { Future<void> reset(String ref) async {
git.run( await git.run(
<String>['reset', ref, '--hard'], <String>['reset', ref, '--hard'],
'reset to $ref', 'reset to $ref',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
/// Push [commit] to the release channel [branch]. /// Push [commit] to the release channel [branch].
void pushRef({ Future<void> pushRef({
required String fromRef, required String fromRef,
required String remote, required String remote,
required String toRef, required String toRef,
bool force = false, bool force = false,
bool dryRun = false, bool dryRun = false,
}) { }) async {
final List<String> args = <String>[ final List<String> args = <String>[
'push', 'push',
if (force) '--force', if (force) '--force',
...@@ -386,47 +387,48 @@ abstract class Repository { ...@@ -386,47 +387,48 @@ abstract class Repository {
if (dryRun) { if (dryRun) {
stdio.printStatus('About to execute command: `$command`'); stdio.printStatus('About to execute command: `$command`');
} else { } else {
git.run( await git.run(
args, args,
'update the release branch with the commit', 'update the release branch with the commit',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
stdio.printStatus('Executed command: `$command`'); stdio.printStatus('Executed command: `$command`');
} }
} }
String commit( Future<String> commit(
String message, { String message, {
bool addFirst = false, bool addFirst = false,
}) { }) async {
assert(!message.contains("'")); assert(!message.contains("'"));
final bool hasChanges = git.getOutput( final bool hasChanges = (await git.getOutput(
<String>['status', '--porcelain'], <String>['status', '--porcelain'],
'check for uncommitted changes', 'check for uncommitted changes',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
).trim().isNotEmpty; )).trim().isNotEmpty;
if (!hasChanges) { if (!hasChanges) {
throw ConductorException('Tried to commit with message $message but no changes were present'); throw ConductorException(
'Tried to commit with message $message but no changes were present');
} }
if (addFirst) { if (addFirst) {
git.run( await git.run(
<String>['add', '--all'], <String>['add', '--all'],
'add all changes to the index', 'add all changes to the index',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
} }
git.run( await git.run(
<String>['commit', "--message='$message'"], <String>['commit', "--message='$message'"],
'commit changes', 'commit changes',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
return reverseParse('HEAD'); return reverseParse('HEAD');
} }
/// Create an empty commit and return the revision. /// Create an empty commit and return the revision.
@visibleForTesting @visibleForTesting
String authorEmptyCommit([String message = 'An empty commit']) { Future<String> authorEmptyCommit([String message = 'An empty commit']) async {
git.run( await git.run(
<String>[ <String>[
'-c', '-c',
'user.name=Conductor', 'user.name=Conductor',
...@@ -438,7 +440,7 @@ abstract class Repository { ...@@ -438,7 +440,7 @@ abstract class Repository {
"'$message'", "'$message'",
], ],
'create an empty commit', 'create an empty commit',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
return reverseParse('HEAD'); return reverseParse('HEAD');
} }
...@@ -450,7 +452,7 @@ abstract class Repository { ...@@ -450,7 +452,7 @@ abstract class Repository {
/// ///
/// This method is for testing purposes. /// This method is for testing purposes.
@visibleForTesting @visibleForTesting
Repository cloneRepository(String cloneName); Future<Repository> cloneRepository(String cloneName);
} }
class FrameworkRepository extends Repository { class FrameworkRepository extends Repository {
...@@ -500,57 +502,66 @@ class FrameworkRepository extends Repository { ...@@ -500,57 +502,66 @@ 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';
static const String defaultBranch = 'master'; static const String defaultBranch = 'master';
String get cacheDirectory => fileSystem.path.join( Future<CiYaml> get ciYaml async {
checkoutDirectory.path, final CiYaml ciYaml =
CiYaml((await checkoutDirectory).childFile('.ci.yaml'));
return ciYaml;
}
Future<String> get cacheDirectory async {
return fileSystem.path.join(
(await checkoutDirectory).path,
'bin', 'bin',
'cache', 'cache',
); );
}
/// Tag [commit] and push the tag to the remote. /// Tag [commit] and push the tag to the remote.
void tag(String commit, String tagName, String remote) { Future<void> tag(String commit, String tagName, String remote) async {
assert(commit.isNotEmpty); assert(commit.isNotEmpty);
assert(tagName.isNotEmpty); assert(tagName.isNotEmpty);
assert(remote.isNotEmpty); assert(remote.isNotEmpty);
stdio.printStatus('About to tag commit $commit as $tagName...'); stdio.printStatus('About to tag commit $commit as $tagName...');
git.run( await git.run(
<String>['tag', tagName, commit], <String>['tag', tagName, commit],
'tag the commit with the version label', 'tag the commit with the version label',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
stdio.printStatus('Tagging successful.'); stdio.printStatus('Tagging successful.');
stdio.printStatus('About to push $tagName to remote $remote...'); stdio.printStatus('About to push $tagName to remote $remote...');
git.run( await git.run(
<String>['push', remote, tagName], <String>['push', remote, tagName],
'publish the tag to the repo', 'publish the tag to the repo',
workingDirectory: checkoutDirectory.path, workingDirectory: (await checkoutDirectory).path,
); );
stdio.printStatus('Tag push successful.'); stdio.printStatus('Tag push successful.');
} }
@override @override
Repository cloneRepository(String? cloneName) { Future<Repository> cloneRepository(String? cloneName) async {
assert(localUpstream); assert(localUpstream);
cloneName ??= 'clone-of-$name'; cloneName ??= 'clone-of-$name';
return FrameworkRepository( return FrameworkRepository(
checkouts, checkouts,
name: cloneName, name: cloneName,
upstreamRemote: Remote( upstreamRemote: Remote(
name: RemoteName.upstream, url: 'file://${checkoutDirectory.path}/'), name: RemoteName.upstream,
url: 'file://${(await checkoutDirectory).path}/'),
); );
} }
void _ensureToolReady() { Future<void> _ensureToolReady() async {
final File toolsStamp = final File toolsStamp = fileSystem
fileSystem.directory(cacheDirectory).childFile('flutter_tools.stamp'); .directory(await cacheDirectory)
.childFile('flutter_tools.stamp');
if (toolsStamp.existsSync()) { if (toolsStamp.existsSync()) {
final String toolsStampHash = toolsStamp.readAsStringSync().trim(); final String toolsStampHash = toolsStamp.readAsStringSync().trim();
final String repoHeadHash = reverseParse('HEAD'); final String repoHeadHash = await reverseParse('HEAD');
if (toolsStampHash == repoHeadHash) { if (toolsStampHash == repoHeadHash) {
return; return;
} }
...@@ -558,39 +569,38 @@ class FrameworkRepository extends Repository { ...@@ -558,39 +569,38 @@ class FrameworkRepository extends Repository {
stdio.printTrace('Building tool...'); stdio.printTrace('Building tool...');
// Build tool // Build tool
processManager.runSync(<String>[ await processManager.run(<String>[
fileSystem.path.join(checkoutDirectory.path, 'bin', 'flutter'), fileSystem.path.join((await checkoutDirectory).path, 'bin', 'flutter'),
'help', 'help',
]); ]);
} }
io.ProcessResult runFlutter(List<String> args) { Future<io.ProcessResult> runFlutter(List<String> args) async {
_ensureToolReady(); await _ensureToolReady();
return processManager.run(<String>[
return processManager.runSync(<String>[ fileSystem.path.join((await checkoutDirectory).path, 'bin', 'flutter'),
fileSystem.path.join(checkoutDirectory.path, 'bin', 'flutter'),
...args, ...args,
]); ]);
} }
@override @override
void checkout(String ref) { Future<void> checkout(String ref) async {
super.checkout(ref); await super.checkout(ref);
// The tool will overwrite old cached artifacts, but not delete unused // The tool will overwrite old cached artifacts, but not delete unused
// artifacts from a previous version. Thus, delete the entire cache and // artifacts from a previous version. Thus, delete the entire cache and
// re-populate. // re-populate.
final Directory cache = fileSystem.directory(cacheDirectory); final Directory cache = fileSystem.directory(await cacheDirectory);
if (cache.existsSync()) { if (cache.existsSync()) {
stdio.printTrace('Deleting cache...'); stdio.printTrace('Deleting cache...');
cache.deleteSync(recursive: true); cache.deleteSync(recursive: true);
} }
_ensureToolReady(); await _ensureToolReady();
} }
Version flutterVersion() { Future<Version> flutterVersion() async {
// Check version // Check version
final io.ProcessResult result = final io.ProcessResult result =
runFlutter(<String>['--version', '--machine']); await runFlutter(<String>['--version', '--machine']);
final Map<String, dynamic> versionJson = jsonDecode( final Map<String, dynamic> versionJson = jsonDecode(
stdoutToString(result.stdout), stdoutToString(result.stdout),
) as Map<String, dynamic>; ) as Map<String, dynamic>;
...@@ -600,12 +610,12 @@ class FrameworkRepository extends Repository { ...@@ -600,12 +610,12 @@ class FrameworkRepository extends Repository {
/// Update this framework's engine version file. /// Update this framework's engine version file.
/// ///
/// Returns [true] if the version file was updated and a commit is needed. /// Returns [true] if the version file was updated and a commit is needed.
bool updateEngineRevision( Future<bool> updateEngineRevision(
String newEngine, { String newEngine, {
@visibleForTesting File? engineVersionFile, @visibleForTesting File? engineVersionFile,
}) { }) async {
assert(newEngine.isNotEmpty); assert(newEngine.isNotEmpty);
engineVersionFile ??= checkoutDirectory engineVersionFile ??= (await checkoutDirectory)
.childDirectory('bin') .childDirectory('bin')
.childDirectory('internal') .childDirectory('internal')
.childFile('engine.version'); .childFile('engine.version');
...@@ -649,33 +659,33 @@ class HostFrameworkRepository extends FrameworkRepository { ...@@ -649,33 +659,33 @@ class HostFrameworkRepository extends FrameworkRepository {
} }
@override @override
Directory get checkoutDirectory => _checkoutDirectory!; Future<Directory> get checkoutDirectory async => _checkoutDirectory!;
@override @override
void newBranch(String branchName) { Future<void> newBranch(String branchName) async {
throw ConductorException( throw ConductorException(
'newBranch not implemented for the host repository'); 'newBranch not implemented for the host repository');
} }
@override @override
void checkout(String ref) { Future<void> checkout(String ref) async {
throw ConductorException( throw ConductorException(
'checkout not implemented for the host repository'); 'checkout not implemented for the host repository');
} }
@override @override
String cherryPick(String commit) { Future<String> cherryPick(String commit) async {
throw ConductorException( throw ConductorException(
'cherryPick not implemented for the host repository'); 'cherryPick not implemented for the host repository');
} }
@override @override
String reset(String ref) { Future<String> reset(String ref) async {
throw ConductorException('reset not implemented for the host repository'); throw ConductorException('reset not implemented for the host repository');
} }
@override @override
void tag(String commit, String tagName, String remote) { Future<void> tag(String commit, String tagName, String remote) async {
throw ConductorException('tag not implemented for the host repository'); throw ConductorException('tag not implemented for the host repository');
} }
...@@ -691,7 +701,7 @@ class HostFrameworkRepository extends FrameworkRepository { ...@@ -691,7 +701,7 @@ class HostFrameworkRepository extends FrameworkRepository {
} }
@override @override
String authorEmptyCommit([String message = 'An empty commit']) { Future<String> authorEmptyCommit([String message = 'An empty commit']) async {
throw ConductorException( throw ConductorException(
'authorEmptyCommit not implemented for the host repository', 'authorEmptyCommit not implemented for the host repository',
); );
...@@ -724,18 +734,21 @@ class EngineRepository extends Repository { ...@@ -724,18 +734,21 @@ class EngineRepository extends Repository {
final Checkouts checkouts; final Checkouts checkouts;
late final CiYaml ciYaml = CiYaml(checkoutDirectory.childFile('.ci.yaml')); Future<CiYaml> get ciYaml async {
final CiYaml ciYaml = CiYaml((await checkoutDirectory).childFile('.ci.yaml'));
return ciYaml;
}
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';
/// Update the `dart_revision` entry in the DEPS file. /// Update the `dart_revision` entry in the DEPS file.
void updateDartRevision( Future<void> updateDartRevision(
String newRevision, { String newRevision, {
@visibleForTesting File? depsFile, @visibleForTesting File? depsFile,
}) { }) async {
assert(newRevision.length == 40); assert(newRevision.length == 40);
depsFile ??= checkoutDirectory.childFile('DEPS'); depsFile ??= (await checkoutDirectory).childFile('DEPS');
final String fileContent = depsFile.readAsStringSync(); final String fileContent = depsFile.readAsStringSync();
final RegExp dartPattern = RegExp("[ ]+'dart_revision': '([a-z0-9]{40})',"); final RegExp dartPattern = RegExp("[ ]+'dart_revision': '([a-z0-9]{40})',");
final Iterable<RegExpMatch> allMatches = final Iterable<RegExpMatch> allMatches =
...@@ -755,14 +768,15 @@ class EngineRepository extends Repository { ...@@ -755,14 +768,15 @@ class EngineRepository extends Repository {
} }
@override @override
Repository cloneRepository(String? cloneName) { Future<Repository> cloneRepository(String? cloneName) async {
assert(localUpstream); assert(localUpstream);
cloneName ??= 'clone-of-$name'; cloneName ??= 'clone-of-$name';
return EngineRepository( return EngineRepository(
checkouts, checkouts,
name: cloneName, name: cloneName,
upstreamRemote: Remote( upstreamRemote: Remote(
name: RemoteName.upstream, url: 'file://${checkoutDirectory.path}/'), name: RemoteName.upstream,
url: 'file://${(await checkoutDirectory).path}/'),
); );
} }
} }
......
...@@ -90,8 +90,8 @@ class RollDevCommand extends Command<void> { ...@@ -90,8 +90,8 @@ class RollDevCommand extends Command<void> {
'For publishing a dev release without cherry picks.'; 'For publishing a dev release without cherry picks.';
@override @override
void run() { Future<void> run() async {
rollDev( await rollDev(
argResults: argResults!, argResults: argResults!,
repository: FrameworkRepository(checkouts), repository: FrameworkRepository(checkouts),
stdio: stdio, stdio: stdio,
...@@ -104,12 +104,12 @@ class RollDevCommand extends Command<void> { ...@@ -104,12 +104,12 @@ class RollDevCommand extends Command<void> {
/// ///
/// Returns true if publishing was successful, else false. /// Returns true if publishing was successful, else false.
@visibleForTesting @visibleForTesting
bool rollDev({ Future<bool> rollDev({
required String usage, required String usage,
required ArgResults argResults, required ArgResults argResults,
required Stdio stdio, required Stdio stdio,
required FrameworkRepository repository, required FrameworkRepository repository,
}) { }) async {
final String remoteName = argResults[kRemoteName] as String; final String remoteName = argResults[kRemoteName] as String;
final String? level = argResults[kIncrement] as String?; final String? level = argResults[kIncrement] as String?;
final String candidateBranch = argResults[kCandidateBranch] as String; final String candidateBranch = argResults[kCandidateBranch] as String;
...@@ -124,30 +124,30 @@ bool rollDev({ ...@@ -124,30 +124,30 @@ bool rollDev({
'and roll a new dev build.\n$usage'); 'and roll a new dev build.\n$usage');
} }
final String remoteUrl = repository.remoteUrl(remoteName); final String remoteUrl = await repository.remoteUrl(remoteName);
if (!repository.gitCheckoutClean()) { if (!(await repository.gitCheckoutClean())) {
throw Exception( throw Exception(
'Your git repository is not clean. Try running "git clean -fd". Warning, ' 'Your git repository is not clean. Try running "git clean -fd". Warning, '
'this will delete files! Run with -n to find out which ones.'); 'this will delete files! Run with -n to find out which ones.');
} }
repository.fetch(remoteName); await repository.fetch(remoteName);
// Verify [commit] is valid // Verify [commit] is valid
final String commit = repository.reverseParse(candidateBranch); final String commit = await repository.reverseParse(candidateBranch);
stdio.printStatus('remoteName is $remoteName'); stdio.printStatus('remoteName is $remoteName');
// Get the name of the last dev release // Get the name of the last dev release
final Version lastVersion = Version.fromString( final Version lastVersion = Version.fromString(
repository.getFullTag(remoteName, 'dev'), await repository.getFullTag(remoteName, 'dev'),
); );
final Version version = final Version version =
skipTagging ? lastVersion : Version.fromCandidateBranch(candidateBranch); skipTagging ? lastVersion : Version.fromCandidateBranch(candidateBranch);
final String tagName = version.toString(); final String tagName = version.toString();
if (repository.reverseParse(lastVersion.toString()).contains(commit.trim())) { if ((await repository.reverseParse(lastVersion.toString())).contains(commit.trim())) {
throw Exception( throw Exception(
'Commit $commit is already on the dev branch as $lastVersion.'); 'Commit $commit is already on the dev branch as $lastVersion.');
} }
...@@ -157,18 +157,18 @@ bool rollDev({ ...@@ -157,18 +157,18 @@ bool rollDev({
return false; return false;
} }
if (skipTagging && !repository.isCommitTagged(commit)) { if (skipTagging && !(await repository.isCommitTagged(commit))) {
throw Exception( throw Exception(
'The $kSkipTagging flag is only supported for tagged commits.'); 'The $kSkipTagging flag is only supported for tagged commits.');
} }
if (!force && !repository.isAncestor(commit, lastVersion.toString())) { if (!force && !(await repository.isAncestor(commit, lastVersion.toString()))) {
throw Exception( throw Exception(
'The previous dev tag $lastVersion is not a direct ancestor of $commit.\n' 'The previous dev tag $lastVersion is not a direct ancestor of $commit.\n'
'The flag "$kForce" is required to force push a new release past a cherry-pick.'); 'The flag "$kForce" is required to force push a new release past a cherry-pick.');
} }
final String hash = repository.reverseParse(commit); final String hash = await repository.reverseParse(commit);
// [commit] can be a prefix for [hash]. // [commit] can be a prefix for [hash].
assert(hash.startsWith(commit)); assert(hash.startsWith(commit));
...@@ -188,10 +188,10 @@ bool rollDev({ ...@@ -188,10 +188,10 @@ bool rollDev({
} }
if (!skipTagging) { if (!skipTagging) {
repository.tag(commit, version.toString(), remoteName); await repository.tag(commit, version.toString(), remoteName);
} }
repository.pushRef( await repository.pushRef(
fromRef: commit, fromRef: commit,
remote: remoteName, remote: remoteName,
toRef: 'dev', toRef: 'dev',
......
...@@ -101,14 +101,6 @@ class StartCommand extends Command<void> { ...@@ -101,14 +101,6 @@ class StartCommand extends Command<void> {
'n': 'Indicates a hotfix to a dev or beta release.', 'n': 'Indicates a hotfix to a dev or beta release.',
}, },
); );
final Git git = Git(processManager);
conductorVersion = git.getOutput(
<String>['rev-parse', 'HEAD'],
'look up the current revision.',
workingDirectory: flutterRoot.path,
).trim();
assert(conductorVersion.isNotEmpty);
} }
final Checkouts checkouts; final Checkouts checkouts;
...@@ -132,7 +124,15 @@ class StartCommand extends Command<void> { ...@@ -132,7 +124,15 @@ class StartCommand extends Command<void> {
String get description => 'Initialize a new Flutter release.'; String get description => 'Initialize a new Flutter release.';
@override @override
void run() { Future<void> run() async {
final Git git = Git(processManager);
conductorVersion = (await git.getOutput(
<String>['rev-parse', 'HEAD'],
'look up the current revision.',
workingDirectory: flutterRoot.path,
)).trim();
assert(conductorVersion.isNotEmpty);
final ArgResults argumentResults = argResults!; final ArgResults argumentResults = argResults!;
if (!platform.isMacOS && !platform.isLinux) { if (!platform.isMacOS && !platform.isLinux) {
throw ConductorException( throw ConductorException(
...@@ -231,42 +231,42 @@ class StartCommand extends Command<void> { ...@@ -231,42 +231,42 @@ class StartCommand extends Command<void> {
// Create a new branch so that we don't accidentally push to upstream // Create a new branch so that we don't accidentally push to upstream
// candidateBranch. // candidateBranch.
final String workingBranchName = 'cherrypicks-$candidateBranch'; final String workingBranchName = 'cherrypicks-$candidateBranch';
engine.newBranch(workingBranchName); await engine.newBranch(workingBranchName);
if (dartRevision != null && dartRevision.isNotEmpty) { if (dartRevision != null && dartRevision.isNotEmpty) {
engine.updateDartRevision(dartRevision); await engine.updateDartRevision(dartRevision);
engine.commit('Update Dart SDK to $dartRevision', addFirst: true); await engine.commit('Update Dart SDK to $dartRevision', addFirst: true);
} }
final List<pb.Cherrypick> engineCherrypicks = _sortCherrypicks( final List<pb.Cherrypick> engineCherrypicks = (await _sortCherrypicks(
repository: engine, repository: engine,
cherrypicks: engineCherrypickRevisions, cherrypicks: engineCherrypickRevisions,
upstreamRef: EngineRepository.defaultBranch, upstreamRef: EngineRepository.defaultBranch,
releaseRef: candidateBranch, releaseRef: candidateBranch,
).map((String revision) => pb.Cherrypick( )).map((String revision) => pb.Cherrypick(
trunkRevision: revision, trunkRevision: revision,
state: pb.CherrypickState.PENDING, state: pb.CherrypickState.PENDING,
)).toList(); )).toList();
for (final pb.Cherrypick cherrypick in engineCherrypicks) { for (final pb.Cherrypick cherrypick in engineCherrypicks) {
final String revision = cherrypick.trunkRevision; final String revision = cherrypick.trunkRevision;
final bool success = engine.canCherryPick(revision); final bool success = await engine.canCherryPick(revision);
stdio.printTrace( stdio.printTrace(
'Attempt to cherrypick $revision ${success ? 'succeeded' : 'failed'}', 'Attempt to cherrypick $revision ${success ? 'succeeded' : 'failed'}',
); );
if (success) { if (success) {
engine.cherryPick(revision); await engine.cherryPick(revision);
cherrypick.state = pb.CherrypickState.COMPLETED; cherrypick.state = pb.CherrypickState.COMPLETED;
} else { } else {
cherrypick.state = pb.CherrypickState.PENDING_WITH_CONFLICT; cherrypick.state = pb.CherrypickState.PENDING_WITH_CONFLICT;
} }
} }
final String engineHead = engine.reverseParse('HEAD'); final String engineHead = await engine.reverseParse('HEAD');
state.engine = pb.Repository( state.engine = pb.Repository(
candidateBranch: candidateBranch, candidateBranch: candidateBranch,
workingBranch: workingBranchName, workingBranch: workingBranchName,
startingGitHead: engineHead, startingGitHead: engineHead,
currentGitHead: engineHead, currentGitHead: engineHead,
checkoutPath: engine.checkoutDirectory.path, checkoutPath: (await engine.checkoutDirectory).path,
cherrypicks: engineCherrypicks, cherrypicks: engineCherrypicks,
dartRevision: dartRevision, dartRevision: dartRevision,
upstream: pb.Remote(name: 'upstream', url: engine.upstreamRemote.url), upstream: pb.Remote(name: 'upstream', url: engine.upstreamRemote.url),
...@@ -284,25 +284,25 @@ class StartCommand extends Command<void> { ...@@ -284,25 +284,25 @@ class StartCommand extends Command<void> {
url: frameworkMirror, url: frameworkMirror,
), ),
); );
framework.newBranch(workingBranchName); await framework.newBranch(workingBranchName);
final List<pb.Cherrypick> frameworkCherrypicks = _sortCherrypicks( final List<pb.Cherrypick> frameworkCherrypicks = (await _sortCherrypicks(
repository: framework, repository: framework,
cherrypicks: frameworkCherrypickRevisions, cherrypicks: frameworkCherrypickRevisions,
upstreamRef: FrameworkRepository.defaultBranch, upstreamRef: FrameworkRepository.defaultBranch,
releaseRef: candidateBranch, releaseRef: candidateBranch,
).map((String revision) => pb.Cherrypick( )).map((String revision) => pb.Cherrypick(
trunkRevision: revision, trunkRevision: revision,
state: pb.CherrypickState.PENDING, state: pb.CherrypickState.PENDING,
)).toList(); )).toList();
for (final pb.Cherrypick cherrypick in frameworkCherrypicks) { for (final pb.Cherrypick cherrypick in frameworkCherrypicks) {
final String revision = cherrypick.trunkRevision; final String revision = cherrypick.trunkRevision;
final bool success = framework.canCherryPick(revision); final bool success = await framework.canCherryPick(revision);
stdio.printTrace( stdio.printTrace(
'Attempt to cherrypick $cherrypick ${success ? 'succeeded' : 'failed'}', 'Attempt to cherrypick $cherrypick ${success ? 'succeeded' : 'failed'}',
); );
if (success) { if (success) {
framework.cherryPick(revision); await framework.cherryPick(revision);
cherrypick.state = pb.CherrypickState.COMPLETED; cherrypick.state = pb.CherrypickState.COMPLETED;
} else { } else {
cherrypick.state = pb.CherrypickState.PENDING_WITH_CONFLICT; cherrypick.state = pb.CherrypickState.PENDING_WITH_CONFLICT;
...@@ -310,7 +310,9 @@ class StartCommand extends Command<void> { ...@@ -310,7 +310,9 @@ class StartCommand extends Command<void> {
} }
// Get framework version // Get framework version
final Version lastVersion = Version.fromString(framework.getFullTag(framework.upstreamRemote.name, candidateBranch, exact: false)); final Version lastVersion = Version.fromString(await framework.getFullTag(
framework.upstreamRemote.name, candidateBranch,
exact: false));
Version nextVersion; Version nextVersion;
if (incrementLetter == 'm') { if (incrementLetter == 'm') {
nextVersion = Version.fromCandidateBranch(candidateBranch); nextVersion = Version.fromCandidateBranch(candidateBranch);
...@@ -333,13 +335,13 @@ class StartCommand extends Command<void> { ...@@ -333,13 +335,13 @@ class StartCommand extends Command<void> {
} }
state.releaseVersion = nextVersion.toString(); state.releaseVersion = nextVersion.toString();
final String frameworkHead = framework.reverseParse('HEAD'); final String frameworkHead = await framework.reverseParse('HEAD');
state.framework = pb.Repository( state.framework = pb.Repository(
candidateBranch: candidateBranch, candidateBranch: candidateBranch,
workingBranch: workingBranchName, workingBranch: workingBranchName,
startingGitHead: frameworkHead, startingGitHead: frameworkHead,
currentGitHead: frameworkHead, currentGitHead: frameworkHead,
checkoutPath: framework.checkoutDirectory.path, checkoutPath: (await framework.checkoutDirectory).path,
cherrypicks: frameworkCherrypicks, cherrypicks: frameworkCherrypicks,
upstream: pb.Remote(name: 'upstream', url: framework.upstreamRemote.url), upstream: pb.Remote(name: 'upstream', url: framework.upstreamRemote.url),
mirror: pb.Remote(name: 'mirror', url: framework.mirrorRemote!.url), mirror: pb.Remote(name: 'mirror', url: framework.mirrorRemote!.url),
...@@ -357,12 +359,12 @@ class StartCommand extends Command<void> { ...@@ -357,12 +359,12 @@ class StartCommand extends Command<void> {
} }
// To minimize merge conflicts, sort the commits by rev-list order. // To minimize merge conflicts, sort the commits by rev-list order.
List<String> _sortCherrypicks({ Future<List<String>> _sortCherrypicks({
required Repository repository, required Repository repository,
required List<String> cherrypicks, required List<String> cherrypicks,
required String upstreamRef, required String upstreamRef,
required String releaseRef, required String releaseRef,
}) { }) async {
if (cherrypicks.isEmpty) { if (cherrypicks.isEmpty) {
return cherrypicks; return cherrypicks;
} }
...@@ -375,7 +377,7 @@ class StartCommand extends Command<void> { ...@@ -375,7 +377,7 @@ class StartCommand extends Command<void> {
final List<String> sortedCherrypicks = <String>[]; final List<String> sortedCherrypicks = <String>[];
for (final String cherrypick in cherrypicks) { for (final String cherrypick in cherrypicks) {
try { try {
final String fullRef = repository.reverseParse(cherrypick); final String fullRef = await repository.reverseParse(cherrypick);
validatedCherrypicks.add(fullRef); validatedCherrypicks.add(fullRef);
} on GitException { } on GitException {
// Catch this exception so that we can validate the rest. // Catch this exception so that we can validate the rest.
...@@ -383,16 +385,16 @@ class StartCommand extends Command<void> { ...@@ -383,16 +385,16 @@ class StartCommand extends Command<void> {
} }
} }
final String branchPoint = repository.branchPoint( final String branchPoint = await repository.branchPoint(
'${repository.upstreamRemote.name}/$upstreamRef', '${repository.upstreamRemote.name}/$upstreamRef',
'${repository.upstreamRemote.name}/$releaseRef', '${repository.upstreamRemote.name}/$releaseRef',
); );
// `git rev-list` returns newest first, so reverse this list // `git rev-list` returns newest first, so reverse this list
final List<String> upstreamRevlist = repository.revList(<String>[ final List<String> upstreamRevlist = (await repository.revList(<String>[
'--ancestry-path', '--ancestry-path',
'$branchPoint..$upstreamRef', '$branchPoint..$upstreamRef',
]).reversed.toList(); ])).reversed.toList();
stdio.printStatus('upstreamRevList:\n${upstreamRevlist.join('\n')}\n'); stdio.printStatus('upstreamRevList:\n${upstreamRevlist.join('\n')}\n');
stdio.printStatus('validatedCherrypicks:\n${validatedCherrypicks.join('\n')}\n'); stdio.printStatus('validatedCherrypicks:\n${validatedCherrypicks.join('\n')}\n');
......
...@@ -56,8 +56,8 @@ void main() { ...@@ -56,8 +56,8 @@ void main() {
); );
final FakeCodesignCommand command = FakeCodesignCommand( final FakeCodesignCommand command = FakeCodesignCommand(
checkouts: checkouts, checkouts: checkouts,
binariesWithEntitlements: binariesWithEntitlements, binariesWithEntitlements: Future<List<String>>.value(binariesWithEntitlements),
binariesWithoutEntitlements: binariesWithoutEntitlements, binariesWithoutEntitlements: Future<List<String>>.value(binariesWithoutEntitlements),
flutterRoot: fileSystem.directory(flutterRoot), flutterRoot: fileSystem.directory(flutterRoot),
); );
runner = CommandRunner<void>('codesign-test', '') runner = CommandRunner<void>('codesign-test', '')
...@@ -326,8 +326,8 @@ void main() { ...@@ -326,8 +326,8 @@ void main() {
), ),
...codesignCheckCommands, ...codesignCheckCommands,
]); ]);
expect( await expectLater(
() async => runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]), () => runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]),
throwsExceptionWith('Test failed because unsigned binaries detected.'), throwsExceptionWith('Test failed because unsigned binaries detected.'),
); );
expect(processManager.hasRemainingExpectations, false); expect(processManager.hasRemainingExpectations, false);
...@@ -413,8 +413,8 @@ void main() { ...@@ -413,8 +413,8 @@ void main() {
), ),
...codesignCheckCommands, ...codesignCheckCommands,
]); ]);
expect( await expectLater(
() async => runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]), () => runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]),
throwsExceptionWith('Test failed because files found with the wrong entitlements'), throwsExceptionWith('Test failed because files found with the wrong entitlements'),
); );
expect(processManager.hasRemainingExpectations, false); expect(processManager.hasRemainingExpectations, false);
...@@ -495,8 +495,8 @@ class FakeCodesignCommand extends CodesignCommand { ...@@ -495,8 +495,8 @@ class FakeCodesignCommand extends CodesignCommand {
}) : super(checkouts: checkouts, flutterRoot: flutterRoot); }) : super(checkouts: checkouts, flutterRoot: flutterRoot);
@override @override
final List<String> binariesWithEntitlements; final Future<List<String>> binariesWithEntitlements;
@override @override
final List<String> binariesWithoutEntitlements; final Future<List<String>> binariesWithoutEntitlements;
} }
...@@ -25,7 +25,7 @@ void main() { ...@@ -25,7 +25,7 @@ void main() {
); );
}); });
test('canCherryPick returns true if git cherry-pick returns 0', () { test('canCherryPick returns true if git cherry-pick returns 0', () async {
const String commit = 'abc123'; const String commit = 'abc123';
final TestStdio stdio = TestStdio(); final TestStdio stdio = TestStdio();
...@@ -73,10 +73,10 @@ void main() { ...@@ -73,10 +73,10 @@ void main() {
stdio: stdio, stdio: stdio,
); );
final Repository repository = FrameworkRepository(checkouts); final Repository repository = FrameworkRepository(checkouts);
expect(repository.canCherryPick(commit), true); expect(await repository.canCherryPick(commit), true);
}); });
test('canCherryPick returns false if git cherry-pick returns non-zero', () { test('canCherryPick returns false if git cherry-pick returns non-zero', () async {
const String commit = 'abc123'; const String commit = 'abc123';
final TestStdio stdio = TestStdio(); final TestStdio stdio = TestStdio();
...@@ -128,10 +128,10 @@ void main() { ...@@ -128,10 +128,10 @@ void main() {
stdio: stdio, stdio: stdio,
); );
final Repository repository = FrameworkRepository(checkouts); final Repository repository = FrameworkRepository(checkouts);
expect(repository.canCherryPick(commit), false); expect(await repository.canCherryPick(commit), false);
}); });
test('cherryPick() applies the commit', () { test('cherryPick() applies the commit', () async {
const String commit = 'abc123'; const String commit = 'abc123';
final TestStdio stdio = TestStdio(); final TestStdio stdio = TestStdio();
...@@ -172,11 +172,11 @@ void main() { ...@@ -172,11 +172,11 @@ void main() {
stdio: stdio, stdio: stdio,
); );
final Repository repository = FrameworkRepository(checkouts); final Repository repository = FrameworkRepository(checkouts);
repository.cherryPick(commit); await repository.cherryPick(commit);
expect(processManager.hasRemainingExpectations, false); expect(processManager.hasRemainingExpectations, false);
}); });
test('updateDartRevision() updates the DEPS file', () { test('updateDartRevision() updates the DEPS file', () async {
const String previousDartRevision = '171876a4e6cf56ee6da1f97d203926bd7afda7ef'; const String previousDartRevision = '171876a4e6cf56ee6da1f97d203926bd7afda7ef';
const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e'; const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
final TestStdio stdio = TestStdio(); final TestStdio stdio = TestStdio();
...@@ -194,7 +194,7 @@ void main() { ...@@ -194,7 +194,7 @@ void main() {
final EngineRepository repo = EngineRepository(checkouts); final EngineRepository repo = EngineRepository(checkouts);
final File depsFile = fileSystem.file('/DEPS'); final File depsFile = fileSystem.file('/DEPS');
depsFile.writeAsStringSync(generateMockDeps(previousDartRevision)); depsFile.writeAsStringSync(generateMockDeps(previousDartRevision));
repo.updateDartRevision(nextDartRevision, depsFile: depsFile); await repo.updateDartRevision(nextDartRevision, depsFile: depsFile);
final String updatedDepsFileContent = depsFile.readAsStringSync(); final String updatedDepsFileContent = depsFile.readAsStringSync();
expect(updatedDepsFileContent, generateMockDeps(nextDartRevision)); expect(updatedDepsFileContent, generateMockDeps(nextDartRevision));
}); });
...@@ -219,7 +219,7 @@ void main() { ...@@ -219,7 +219,7 @@ void main() {
vars = { vars = {
}'''); }''');
expect( expect(
() => repo.updateDartRevision(nextDartRevision, depsFile: depsFile), () async => repo.updateDartRevision(nextDartRevision, depsFile: depsFile),
throwsExceptionWith('Unexpected content in the DEPS file at'), throwsExceptionWith('Unexpected content in the DEPS file at'),
); );
}); });
...@@ -278,7 +278,7 @@ vars = { ...@@ -278,7 +278,7 @@ vars = {
final EngineRepository repo = EngineRepository(checkouts); final EngineRepository repo = EngineRepository(checkouts);
expect( expect(
() => repo.commit(message), () async => repo.commit(message),
throwsExceptionWith('Tried to commit with message $message but no changes were present'), throwsExceptionWith('Tried to commit with message $message but no changes were present'),
); );
}); });
...@@ -338,7 +338,7 @@ vars = { ...@@ -338,7 +338,7 @@ vars = {
repo.commit(message); repo.commit(message);
}); });
test('updateEngineRevision() returns false if newCommit is the same as version file', () { test('updateEngineRevision() returns false if newCommit is the same as version file', () async {
const String commit1 = 'abc123'; const String commit1 = 'abc123';
const String commit2 = 'def456'; const String commit2 = 'def456';
final TestStdio stdio = TestStdio(); final TestStdio stdio = TestStdio();
...@@ -371,7 +371,7 @@ vars = { ...@@ -371,7 +371,7 @@ vars = {
); );
final FrameworkRepository repo = FrameworkRepository(checkouts); final FrameworkRepository repo = FrameworkRepository(checkouts);
final bool didUpdate = repo.updateEngineRevision(commit2, engineVersionFile: engineVersionFile); final bool didUpdate = await repo.updateEngineRevision(commit2, engineVersionFile: engineVersionFile);
expect(didUpdate, false); expect(didUpdate, false);
}); });
...@@ -385,7 +385,7 @@ vars = { ...@@ -385,7 +385,7 @@ vars = {
); );
}); });
test('ciYaml.enableBranch() will prepend the given branch to the yaml list of enabled_branches', () { test('ciYaml.enableBranch() will prepend the given branch to the yaml list of enabled_branches', () async {
const String commit1 = 'abc123'; const String commit1 = 'abc123';
final TestStdio stdio = TestStdio(); final TestStdio stdio = TestStdio();
final MemoryFileSystem fileSystem = MemoryFileSystem.test(); final MemoryFileSystem fileSystem = MemoryFileSystem.test();
...@@ -430,18 +430,18 @@ enabled_branches: ...@@ -430,18 +430,18 @@ enabled_branches:
final FrameworkRepository framework = FrameworkRepository(checkouts); final FrameworkRepository framework = FrameworkRepository(checkouts);
expect( expect(
framework.ciYaml.enabledBranches, (await framework.ciYaml).enabledBranches,
<String>['master', 'dev', 'beta', 'stable'], <String>['master', 'dev', 'beta', 'stable'],
); );
framework.ciYaml.enableBranch('foo'); (await framework.ciYaml).enableBranch('foo');
expect( expect(
framework.ciYaml.enabledBranches, (await framework.ciYaml).enabledBranches,
<String>['foo', 'master', 'dev', 'beta', 'stable'], <String>['foo', 'master', 'dev', 'beta', 'stable'],
); );
expect( expect(
framework.ciYaml.stringContents, (await framework.ciYaml).stringContents,
''' '''
# Friendly note # Friendly note
...@@ -498,7 +498,7 @@ enabled_branches: ...@@ -498,7 +498,7 @@ enabled_branches:
final FrameworkRepository framework = FrameworkRepository(checkouts); final FrameworkRepository framework = FrameworkRepository(checkouts);
expect( expect(
() => framework.ciYaml.enableBranch('master'), () async => (await framework.ciYaml).enableBranch('master'),
throwsExceptionWith('.ci.yaml already contains the branch master'), throwsExceptionWith('.ci.yaml already contains the branch master'),
); );
}); });
......
...@@ -51,7 +51,7 @@ void main() { ...@@ -51,7 +51,7 @@ void main() {
remote: remote, remote: remote,
); );
expect( expect(
() => rollDev( () async => rollDev(
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
stdio: stdio, stdio: stdio,
...@@ -95,7 +95,7 @@ void main() { ...@@ -95,7 +95,7 @@ void main() {
remote: remote, remote: remote,
); );
expect( expect(
() => rollDev( () async => rollDev(
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
stdio: stdio, stdio: stdio,
...@@ -105,7 +105,7 @@ void main() { ...@@ -105,7 +105,7 @@ void main() {
); );
}); });
test('does not reset or tag if --just-print is specified', () { test('does not reset or tag if --just-print is specified', () async {
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>[ const FakeCommand(command: <String>[
'git', 'git',
...@@ -166,7 +166,7 @@ void main() { ...@@ -166,7 +166,7 @@ void main() {
justPrint: true, justPrint: true,
); );
expect( expect(
rollDev( await rollDev(
usage: usage, usage: usage,
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
...@@ -249,7 +249,7 @@ void main() { ...@@ -249,7 +249,7 @@ void main() {
skipTagging: true, skipTagging: true,
); );
expect( expect(
() => rollDev( () async => rollDev(
usage: usage, usage: usage,
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
...@@ -320,7 +320,7 @@ void main() { ...@@ -320,7 +320,7 @@ void main() {
justPrint: true, justPrint: true,
); );
expect( expect(
() => rollDev( () async => rollDev(
usage: usage, usage: usage,
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
...@@ -403,7 +403,7 @@ void main() { ...@@ -403,7 +403,7 @@ void main() {
const String errorMessage = 'The previous dev tag $lastVersion is not a ' const String errorMessage = 'The previous dev tag $lastVersion is not a '
'direct ancestor of $commit.'; 'direct ancestor of $commit.';
expect( expect(
() => rollDev( () async => rollDev(
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
stdio: stdio, stdio: stdio,
...@@ -413,7 +413,7 @@ void main() { ...@@ -413,7 +413,7 @@ void main() {
); );
}); });
test('does not tag but updates branch if --skip-tagging provided', () { test('does not tag but updates branch if --skip-tagging provided', () async {
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>[ const FakeCommand(command: <String>[
'git', 'git',
...@@ -498,7 +498,7 @@ void main() { ...@@ -498,7 +498,7 @@ void main() {
skipTagging: true, skipTagging: true,
); );
expect( expect(
rollDev( await rollDev(
usage: usage, usage: usage,
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
...@@ -508,7 +508,7 @@ void main() { ...@@ -508,7 +508,7 @@ void main() {
); );
}); });
test('successfully tags and publishes release', () { test('successfully tags and publishes release', () async {
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>[ const FakeCommand(command: <String>[
'git', 'git',
...@@ -597,7 +597,7 @@ void main() { ...@@ -597,7 +597,7 @@ void main() {
remote: remote, remote: remote,
); );
expect( expect(
rollDev( await rollDev(
usage: usage, usage: usage,
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
...@@ -607,7 +607,7 @@ void main() { ...@@ -607,7 +607,7 @@ void main() {
); );
}); });
test('successfully publishes release with --force', () { test('successfully publishes release with --force', () async {
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>[ const FakeCommand(command: <String>[
'git', 'git',
...@@ -692,7 +692,7 @@ void main() { ...@@ -692,7 +692,7 @@ void main() {
force: true, force: true,
); );
expect( expect(
rollDev( await rollDev(
argResults: fakeArgResults, argResults: fakeArgResults,
repository: repo, repository: repo,
stdio: stdio, stdio: stdio,
......
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