Unverified Commit 7b3ce8c0 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

[flutter_conductor] Add candidates sub command (#81234)

parent 279f40d4
......@@ -9,6 +9,7 @@
import 'dart:io' as io;
import 'package:args/command_runner.dart';
import 'package:dev_tools/candidates.dart';
import 'package:dev_tools/clean.dart';
import 'package:dev_tools/codesign.dart';
import 'package:dev_tools/globals.dart';
......@@ -66,6 +67,10 @@ Future<void> main(List<String> args) async {
CleanCommand(
checkouts: checkouts,
),
CandidatesCommand(
checkouts: checkouts,
flutterRoot: localFlutterRoot,
),
].forEach(runner.addCommand);
if (!assertsEnabled()) {
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import './git.dart';
import './globals.dart' show releaseCandidateBranchRegex;
import './repository.dart';
import './stdio.dart';
import './version.dart';
const String kRemote = 'remote';
class CandidatesCommand extends Command<void> {
CandidatesCommand({
required this.flutterRoot,
required this.checkouts,
}) : git = Git(checkouts.processManager), stdio = checkouts.stdio {
argParser.addOption(
kRemote,
help: 'Which remote name to query for branches.',
defaultsTo: 'upstream',
);
}
final Checkouts checkouts;
final Directory flutterRoot;
final Git git;
final Stdio stdio;
@override
String get name => 'candidates';
@override
String get description => 'List release candidates.';
@override
void run() {
final ArgResults results = argResults!;
git.run(
<String>['fetch', results[kRemote] as String],
'Fetch from remote ${results[kRemote]}',
workingDirectory: flutterRoot.path,
);
final FrameworkRepository framework = HostFrameworkRepository(
checkouts: checkouts,
name: 'framework-for-candidates',
upstreamPath: flutterRoot.path,
);
final Version currentVersion = framework.flutterVersion();
stdio.printStatus('currentVersion = $currentVersion');
final List<String> branches = git.getOutput(
<String>[
'branch',
'--no-color',
'--remotes',
'--list',
'${results[kRemote]}/*',
],
'List all remote branches',
workingDirectory: flutterRoot.path,
).split('\n');
// Pattern for extracting only the branch name via sub-group 1
final RegExp remotePattern = RegExp('${results[kRemote]}\\/(.*)');
for (final String branchName in branches) {
final RegExpMatch? candidateMatch = releaseCandidateBranchRegex.firstMatch(branchName);
if (candidateMatch == null) {
continue;
}
final int currentX = currentVersion.x;
final int currentY = currentVersion.y;
final int currentZ = currentVersion.z;
final int currentM = currentVersion.m ?? 0;
final int x = int.parse(candidateMatch.group(1)!);
final int y = int.parse(candidateMatch.group(2)!);
final int m = int.parse(candidateMatch.group(3)!);
final RegExpMatch match = remotePattern.firstMatch(branchName)!;
if (x < currentVersion.x) {
continue;
}
if (x == currentVersion.x && y < currentVersion.y) {
continue;
}
if (x == currentX && y == currentY && currentZ == 0 && m < currentM) {
continue;
}
stdio.printStatus(match.group(1)!);
}
}
}
......@@ -519,6 +519,75 @@ class FrameworkRepository extends Repository {
}
}
/// A wrapper around the host repository that is executing the conductor.
///
/// [Repository] methods that mutate the underlying repository will throw a
/// [ConductorException].
class HostFrameworkRepository extends FrameworkRepository {
HostFrameworkRepository({
required Checkouts checkouts,
String name = 'host-framework',
bool useExistingCheckout = false,
required String upstreamPath,
}) : super(
checkouts,
name: name,
fetchRemote: Remote(
name: RemoteName.upstream,
url: 'file://$upstreamPath/',
),
localUpstream: false,
useExistingCheckout: useExistingCheckout,
) {
_checkoutDirectory = checkouts.fileSystem.directory(upstreamPath);
}
@override
Directory get checkoutDirectory => _checkoutDirectory!;
@override
void newBranch(String branchName) {
throw ConductorException('newBranch not implemented for the host repository');
}
@override
void checkout(String ref) {
throw ConductorException('checkout not implemented for the host repository');
}
@override
String cherryPick(String commit) {
throw ConductorException('cherryPick not implemented for the host repository');
}
@override
String reset(String ref) {
throw ConductorException('reset not implemented for the host repository');
}
@override
void tag(String commit, String tagName, String remote) {
throw ConductorException('tag not implemented for the host repository');
}
@override
void updateChannel(
String commit,
String remote,
String branch, {
bool force = false,
}) {
throw ConductorException('updateChannel not implemented for the host repository');
}
@override
String authorEmptyCommit([String message = 'An empty commit']) {
throw ConductorException(
'authorEmptyCommit not implemented for the host repository',
);
}
}
class EngineRepository extends Repository {
EngineRepository(
this.checkouts, {
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:dev_tools/candidates.dart';
import 'package:dev_tools/repository.dart';
import 'package:file/memory.dart';
import 'package:platform/platform.dart';
import '../../../packages/flutter_tools/test/src/fake_process_manager.dart';
import './common.dart';
void main() {
group('candidates command', () {
const String flutterRoot = '/flutter';
const String flutterBinPath = '$flutterRoot/bin/flutter';
const String checkoutsParentDirectory = '$flutterRoot/dev/tools/';
const String remoteName = 'origin';
late MemoryFileSystem fileSystem;
late FakePlatform platform;
late TestStdio stdio;
late FakeProcessManager processManager;
final String operatingSystem = const LocalPlatform().operatingSystem;
setUp(() {
stdio = TestStdio();
fileSystem = MemoryFileSystem.test();
});
CommandRunner<void> createRunner({
required Checkouts checkouts,
}) {
final CandidatesCommand command = CandidatesCommand(
checkouts: checkouts,
flutterRoot: fileSystem.directory(flutterRoot),
);
return CommandRunner<void>('clean-test', '')..addCommand(command);
}
test('prints only branches from targeted remote', () async {
const String currentVersion = '1.2.3';
const String branch = 'flutter-1.2-candidate.0';
processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['git', 'fetch', remoteName],
),
const FakeCommand(
command: <String>[flutterBinPath, 'help'],
),
const FakeCommand(
command: <String>[flutterBinPath, '--version', '--machine'],
stdout: '{"frameworkVersion": "$currentVersion"}',
),
FakeCommand(
command: const <String>[
'git',
'branch',
'--no-color',
'--remotes',
'--list',
'$remoteName/*',
],
stdout: <String>[
'other-remote/branch1',
'$remoteName/$branch',
].join('\n'),
),
]);
final String pathSeparator = operatingSystem == 'windows' ? r'\' : '/';
platform = FakePlatform(
environment: <String, String>{
'HOME': <String>['path', 'to', 'home'].join(pathSeparator),
},
pathSeparator: pathSeparator,
);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final CommandRunner<void> runner = createRunner(checkouts: checkouts);
await runner.run(<String>['candidates', '--$kRemote', remoteName]);
expect(stdio.stdout.contains('currentVersion = $currentVersion'), true);
expect(stdio.stdout.contains(branch), true);
expect(stdio.stdout.contains('branch1'), false);
});
}, onPlatform: <String, dynamic>{
'windows': const Skip('Flutter Conductor only supported on macos/linux'),
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment