Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shorebird_cli): add getVersion to ShorebirdFlutter #1073

Merged
merged 2 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a file missing from this PR? This doesn't seem to be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not used yet was going to refactor the code to use it in the next PR to keep the PRs smaller

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