feat(shorebird_cli): add getVersion to ShorebirdFlutter (#1073)

This commit is contained in:
Felix Angelov
2023-08-09 10:30:29 -05:00
committed by GitHub
parent 5d80849c8f
commit 8f52e84792
5 changed files with 114 additions and 19 deletions
@@ -51,8 +51,14 @@ Usage: shorebird flutter versions use <version>''');
}
if (!versions.contains(version)) {
final openIssueLink = link(
uri: Uri.parse(
'https://github.com/shorebirdtech/shorebird/issues/new?assignees=&labels=feature&projects=&template=feature_request.md&title=feat%3A+',
),
message: 'open an issue',
);
logger.err('''
Version $version not found.
Version $version not found. Please $openIssueLink to request a new version.
Use `shorebird flutter versions list` to list available versions.''');
return ExitCode.software.code;
}
@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/git.dart';
import 'package:shorebird_cli/src/process.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
/// A reference to a [ShorebirdFlutter] instance.
@@ -19,6 +20,7 @@ class ShorebirdFlutter {
/// {@macro shorebird_flutter}
const ShorebirdFlutter();
static const executable = 'flutter';
static const String flutterGitUrl =
'https://github.com/shorebirdtech/flutter.git';
@@ -62,6 +64,30 @@ class ShorebirdFlutter {
return status.isEmpty;
}
/// Returns the current Shorebird Flutter version.
/// Throws a [ProcessException] if the version check fails.
/// Returns `null` if the version check succeeds but the version cannot be
/// parsed.
Future<String?> getVersion() async {
const args = ['--version'];
final result = await process.run(executable, args, runInShell: true);
if (result.exitCode != 0) {
throw ProcessException(
executable,
args,
'${result.stderr}',
result.exitCode,
);
}
final output = result.stdout.toString();
final flutterVersionRegex = RegExp(r'Flutter (\d+.\d+.\d+)');
final match = flutterVersionRegex.firstMatch(output);
return match?.group(1);
}
Future<List<String>> getVersions({String? revision}) async {
final result = await git.forEachRef(
format: '%(refname:short)',
@@ -10,7 +10,7 @@ final shorebirdVersionRef = create(ShorebirdVersion.new);
/// The [ShorebirdVersion] instance available in the current zone.
ShorebirdVersion get shorebirdVersion => read(shorebirdVersionRef);
/// {@template shorebird_version_manager}
/// {@template shorebird_version}
/// Provides information about installed and available versions of Shorebird.
/// {@endtemplate}
class ShorebirdVersion {
@@ -106,11 +106,17 @@ Usage: shorebird flutter versions use <version>'''),
runWithOverrides(command.run),
completion(equals(ExitCode.software.code)),
);
final openIssueLink = link(
uri: Uri.parse(
'https://github.com/shorebirdtech/shorebird/issues/new?assignees=&labels=feature&projects=&template=feature_request.md&title=feat%3A+',
),
message: 'open an issue',
);
verifyInOrder([
() => logger.progress('Fetching Flutter versions'),
() => progress.complete(),
() => logger.err('''
Version $version not found.
Version $version not found. Please $openIssueLink to request a new version.
Use `shorebird flutter versions list` to list available versions.'''),
]);
});
@@ -5,6 +5,7 @@ import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/git.dart';
import 'package:shorebird_cli/src/process.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_flutter.dart';
import 'package:test/test.dart';
@@ -13,6 +14,11 @@ class _MockGit extends Mock implements Git {}
class _MockShorebirdEnv extends Mock implements ShorebirdEnv {}
class _MockShorebirdProcess extends Mock implements ShorebirdProcess {}
class _MockShorebirdProcessResult extends Mock
implements ShorebirdProcessResult {}
void main() {
group(ShorebirdFlutter, () {
const flutterRevision = 'flutter-revision';
@@ -20,13 +26,16 @@ void main() {
late Directory flutterDirectory;
late Git git;
late ShorebirdEnv shorebirdEnv;
late ShorebirdFlutter shorebirdFlutterManager;
late ShorebirdProcess process;
late ShorebirdProcessResult processResult;
late ShorebirdFlutter shorebirdFlutter;
R runWithOverrides<R>(R Function() body) {
return runScoped(
body,
values: {
gitRef.overrideWith(() => git),
processRef.overrideWith(() => process),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
},
);
@@ -37,7 +46,9 @@ void main() {
flutterDirectory = Directory(p.join(shorebirdRoot.path, 'flutter'));
git = _MockGit();
shorebirdEnv = _MockShorebirdEnv();
shorebirdFlutterManager = runWithOverrides(ShorebirdFlutter.new);
process = _MockShorebirdProcess();
processResult = _MockShorebirdProcessResult();
shorebirdFlutter = runWithOverrides(ShorebirdFlutter.new);
when(
() => git.clone(
@@ -72,6 +83,52 @@ void main() {
).thenAnswer((_) async => flutterRevision);
when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory);
when(() => shorebirdEnv.flutterRevision).thenReturn(flutterRevision);
when(
() => process.run('flutter', ['--version'], runInShell: true),
).thenAnswer((_) async => processResult);
when(() => processResult.exitCode).thenReturn(0);
});
group('getVersion', () {
test('throws ProcessException when process exits with non-zero code',
() async {
const error = 'oops';
when(() => processResult.exitCode).thenReturn(ExitCode.software.code);
when(() => processResult.stderr).thenReturn(error);
await expectLater(
runWithOverrides(shorebirdFlutter.getVersion),
throwsA(isA<ProcessException>()),
);
verify(
() => process.run('flutter', ['--version'], runInShell: true),
).called(1);
});
test('returns null when cannot parse version', () async {
when(() => processResult.stdout).thenReturn('');
await expectLater(
runWithOverrides(shorebirdFlutter.getVersion),
completion(isNull),
);
verify(
() => process.run('flutter', ['--version'], runInShell: true),
).called(1);
});
test('returns version when able to parse the string', () async {
when(() => processResult.stdout).thenReturn('''
Flutter 3.10.6 • channel stable • git@github.com:flutter/flutter.git
Framework • revision f468f3366c (4 weeks ago) • 2023-07-12 15:19:05 -0700
Engine • revision cdbeda788a
Tools • Dart 3.0.6 • DevTools 2.23.1''');
await expectLater(
runWithOverrides(shorebirdFlutter.getVersion),
completion(equals('3.10.6')),
);
verify(
() => process.run('flutter', ['--version'], runInShell: true),
).called(1);
});
});
group('getVersions', () {
@@ -104,7 +161,7 @@ origin/flutter_release/3.10.6''';
).thenAnswer((_) async => output);
await expectLater(
runWithOverrides(shorebirdFlutterManager.getVersions),
runWithOverrides(shorebirdFlutter.getVersions),
completion(equals(versions)),
);
verify(
@@ -135,7 +192,7 @@ origin/flutter_release/3.10.6''';
);
expect(
runWithOverrides(shorebirdFlutterManager.getVersions),
runWithOverrides(shorebirdFlutter.getVersions),
throwsA(
isA<ProcessException>().having(
(e) => e.message,
@@ -155,7 +212,7 @@ origin/flutter_release/3.10.6''';
).createSync(recursive: true);
await runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
);
verifyNever(
@@ -179,7 +236,7 @@ origin/flutter_release/3.10.6''';
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
),
throwsA(exception),
);
@@ -204,7 +261,7 @@ origin/flutter_release/3.10.6''';
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
),
throwsA(exception),
);
@@ -226,7 +283,7 @@ origin/flutter_release/3.10.6''';
test('completes when clone and checkout succeed', () async {
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
),
completes,
);
@@ -236,7 +293,7 @@ origin/flutter_release/3.10.6''';
group('pruneRemoteOrigin', () {
test('completes when git command exits with code 0', () async {
await expectLater(
runWithOverrides(() => shorebirdFlutterManager.pruneRemoteOrigin()),
runWithOverrides(() => shorebirdFlutter.pruneRemoteOrigin()),
completes,
);
verify(
@@ -252,7 +309,7 @@ origin/flutter_release/3.10.6''';
const customRevision = 'custom-revision';
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.pruneRemoteOrigin(
() => shorebirdFlutter.pruneRemoteOrigin(
revision: customRevision,
),
),
@@ -284,7 +341,7 @@ origin/flutter_release/3.10.6''';
);
expect(
runWithOverrides(() => shorebirdFlutterManager.pruneRemoteOrigin()),
runWithOverrides(() => shorebirdFlutter.pruneRemoteOrigin()),
throwsA(
isA<ProcessException>().having(
(e) => e.message,
@@ -299,7 +356,7 @@ origin/flutter_release/3.10.6''';
group('isPorcelain', () {
test('returns true when status is empty', () async {
await expectLater(
runWithOverrides(() => shorebirdFlutterManager.isPorcelain()),
runWithOverrides(() => shorebirdFlutter.isPorcelain()),
completion(isTrue),
);
verify(
@@ -318,7 +375,7 @@ origin/flutter_release/3.10.6''';
),
).thenAnswer((_) async => 'M some/file');
await expectLater(
runWithOverrides(() => shorebirdFlutterManager.isPorcelain()),
runWithOverrides(() => shorebirdFlutter.isPorcelain()),
completion(isFalse),
);
verify(
@@ -347,7 +404,7 @@ origin/flutter_release/3.10.6''';
);
expect(
runWithOverrides(() => shorebirdFlutterManager.isPorcelain()),
runWithOverrides(() => shorebirdFlutter.isPorcelain()),
throwsA(
isA<ProcessException>().having(
(e) => e.message,
@@ -375,7 +432,7 @@ origin/flutter_release/3.10.6''';
test('installs revision if it does not exist', () async {
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.useVersion(version: version),
() => shorebirdFlutter.useVersion(version: version),
),
completes,
);
@@ -403,7 +460,7 @@ origin/flutter_release/3.10.6''';
.createSync(recursive: true);
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.useVersion(version: version),
() => shorebirdFlutter.useVersion(version: version),
),
completes,
);