feat(shorebird_cli): verify flutter engine revision (#96)
This commit is contained in:
@@ -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()),
|
||||
|
||||
Reference in New Issue
Block a user