Skip to content

Commit

Permalink
feat(shorebird_cli): add getVersion to ShorebirdFlutter (#1073)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel authored Aug 9, 2023
1 parent 5d80849 commit 8f52e84
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
26 changes: 26 additions & 0 deletions packages/shorebird_cli/lib/src/shorebird_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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';

Expand Down Expand Up @@ -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)',
Expand Down
2 changes: 1 addition & 1 deletion packages/shorebird_cli/lib/src/shorebird_version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.'''),
]);
});
Expand Down
89 changes: 73 additions & 16 deletions packages/shorebird_cli/test/src/shorebird_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -13,20 +14,28 @@ 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';
late Directory shorebirdRoot;
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),
},
);
Expand All @@ -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(
Expand Down Expand Up @@ -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', () {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -135,7 +192,7 @@ origin/flutter_release/3.10.6''';
);

expect(
runWithOverrides(shorebirdFlutterManager.getVersions),
runWithOverrides(shorebirdFlutter.getVersions),
throwsA(
isA<ProcessException>().having(
(e) => e.message,
Expand All @@ -155,7 +212,7 @@ origin/flutter_release/3.10.6''';
).createSync(recursive: true);

await runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
);

verifyNever(
Expand All @@ -179,7 +236,7 @@ origin/flutter_release/3.10.6''';

await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
),
throwsA(exception),
);
Expand All @@ -204,7 +261,7 @@ origin/flutter_release/3.10.6''';

await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
() => shorebirdFlutter.installRevision(revision: revision),
),
throwsA(exception),
);
Expand All @@ -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,
);
Expand All @@ -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(
Expand All @@ -252,7 +309,7 @@ origin/flutter_release/3.10.6''';
const customRevision = 'custom-revision';
await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.pruneRemoteOrigin(
() => shorebirdFlutter.pruneRemoteOrigin(
revision: customRevision,
),
),
Expand Down Expand Up @@ -284,7 +341,7 @@ origin/flutter_release/3.10.6''';
);

expect(
runWithOverrides(() => shorebirdFlutterManager.pruneRemoteOrigin()),
runWithOverrides(() => shorebirdFlutter.pruneRemoteOrigin()),
throwsA(
isA<ProcessException>().having(
(e) => e.message,
Expand All @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -347,7 +404,7 @@ origin/flutter_release/3.10.6''';
);

expect(
runWithOverrides(() => shorebirdFlutterManager.isPorcelain()),
runWithOverrides(() => shorebirdFlutter.isPorcelain()),
throwsA(
isA<ProcessException>().having(
(e) => e.message,
Expand Down Expand Up @@ -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,
);
Expand Down Expand Up @@ -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,
);
Expand Down

0 comments on commit 8f52e84

Please sign in to comment.