feat(shorebird_cli): verify flutter engine revision (#96)

This commit is contained in:
Felix Angelov
2023-03-20 16:08:44 -05:00
committed by GitHub
parent f97076e784
commit 201678320f
3 changed files with 129 additions and 2 deletions
@@ -1,15 +1,24 @@
import 'dart:io';
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:cli_completion/cli_completion.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:pub_updater/pub_updater.dart';
import 'package:shorebird_cli/src/commands/commands.dart';
import 'package:shorebird_cli/src/flutter_engine_revision.dart';
import 'package:shorebird_cli/src/version.dart';
const executableName = 'shorebird';
const packageName = 'shorebird_cli';
const description = 'The shorebird command-line tool';
typedef RunProcess = Future<ProcessResult> Function(
String executable,
List<String> arguments, {
bool runInShell,
});
/// {@template shorebird_cli_command_runner}
/// A [CommandRunner] for the CLI.
///
@@ -19,9 +28,13 @@ const description = 'The shorebird command-line tool';
/// {@endtemplate}
class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
/// {@macro shorebird_cli_command_runner}
ShorebirdCliCommandRunner({PubUpdater? pubUpdater, Logger? logger})
: _logger = logger ?? Logger(),
ShorebirdCliCommandRunner({
PubUpdater? pubUpdater,
Logger? logger,
RunProcess? runProcess,
}) : _logger = logger ?? Logger(),
_pubUpdater = pubUpdater ?? PubUpdater(),
_runProcess = runProcess ?? Process.run,
super(executableName, description) {
argParser
..addFlag(
@@ -50,6 +63,7 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
final Logger _logger;
final PubUpdater _pubUpdater;
final RunProcess _runProcess;
@override
Future<int> run(Iterable<String> args) async {
@@ -58,6 +72,30 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
if (topLevelResults['verbose'] == true) {
_logger.level = Level.verbose;
}
try {
final flutterEngineRevision = await _getFlutterEngineRevision();
if (flutterEngineRevision != requiredFlutterEngineRevision) {
_logger.err(
'''
Shorebird only works with the latest stable channel at this time.
To use the latest stable channel, run:
flutter channel stable
flutter upgrade
If you believe you're already on the latest stable channel, please ask on Discord, we're happy to help!
Required engine revision: "$requiredFlutterEngineRevision"
Detected engine revision: "$flutterEngineRevision"''',
);
return ExitCode.software.code;
}
} catch (error) {
_logger.err('Failed to get Flutter engine revision.\n$error');
return ExitCode.software.code;
}
return await runCommand(topLevelResults) ?? ExitCode.success.code;
} on FormatException catch (e, stackTrace) {
// On format errors, show the commands error message, root usage and
@@ -104,6 +142,23 @@ class ShorebirdCliCommandRunner extends CompletionCommandRunner<int> {
return exitCode;
}
Future<String> _getFlutterEngineRevision() async {
final result = await _runProcess(
'flutter',
['--version'],
runInShell: true,
);
if (result.exitCode != 0) throw Exception('${result.stderr}');
final output = result.stdout as String;
final regexp = RegExp(r'Engine • revision (.*?$)', multiLine: true);
final flutterEngineRevision = regexp.firstMatch(output)?.group(1);
if (flutterEngineRevision == null) {
throw Exception('Unable to determine the Flutter engine revision.');
}
return flutterEngineRevision;
}
/// Checks if the current version (set by the build runner on the
/// version.dart file) is the most recent one. If not, show a prompt to the
/// user.
@@ -0,0 +1,3 @@
/// The hash of the Flutter engine required by Shorebird.
/// Currently, Shorebird only supports the latest stable Flutter channel.
const requiredFlutterEngineRevision = '1837b5be5f';
@@ -41,13 +41,82 @@ void main() {
processResult = _MockProcessResult();
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
when(() => processResult.stdout).thenReturn(
'''
Flutter 3.7.7 • channel stable • git@github.com:flutter/flutter.git
Framework • revision 2ad6cd72c0 (12 days ago) • 2023-03-08 09:41:59 -0800
Engine • revision 1837b5be5f
Tools • Dart 2.19.4 • DevTools 2.20.1
''',
);
commandRunner = ShorebirdCliCommandRunner(
logger: logger,
pubUpdater: pubUpdater,
runProcess: (
String executable,
List<String> arguments, {
bool runInShell = false,
}) async {
return processResult;
},
);
});
test('exits when Flutter is not installed', () async {
const error = 'oops something went wrong';
when(() => processResult.exitCode).thenReturn(1);
when(() => processResult.stderr).thenReturn(error);
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.software.code));
verify(() => logger.err(any(that: contains(error)))).called(1);
});
test('exits when unable to detect the Flutter engine revision', () async {
when(() => processResult.exitCode).thenReturn(0);
when(() => processResult.stdout).thenReturn(
'''
Flutter 3.7.7 • channel stable •
Framework • revision 2ad6cd72c0 (12 days ago) • 2023-03-08 09:41:59 -0800
Tools • Dart 2.19.4 • DevTools 2.20.1
''',
);
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.software.code));
verify(
() => logger.err(
any(
that: contains('Unable to determine the Flutter engine revision.'),
),
),
).called(1);
});
test('exits when there is an incompatible Flutter engine', () async {
when(() => processResult.stdout).thenReturn(
'''
Flutter 3.7.7 • channel stable •
Framework • revision 2ad6cd72c0 (12 days ago) • 2023-03-08 09:41:59 -0800
Engine • revision 639e313f99
Tools • Dart 2.19.4 • DevTools 2.20.1
''',
);
final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.software.code));
verify(
() => logger.err(
any(
that: contains(
'''Shorebird only works with the latest stable channel at this time.''',
),
),
),
).called(1);
});
test('shows update message when newer version exists', () async {
when(
() => pubUpdater.getLatestVersion(any()),