feat(shorebird_cli): add xcodeVersion method to xcodebuild wrapper (#1360)
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/process.dart';
|
||||
|
||||
@@ -106,4 +107,41 @@ class XcodeBuild {
|
||||
schemes: schemes,
|
||||
);
|
||||
}
|
||||
|
||||
/// Gets the currently installed version of Xcode.
|
||||
///
|
||||
/// Invokes `xcodebuild -version` and parses the output.
|
||||
/// Output is expected to be of the form:
|
||||
///
|
||||
/// $ /usr/bin/xcodebuild -version
|
||||
/// Xcode 15.0
|
||||
/// Build version 15A240d
|
||||
Future<Version> xcodeVersion() async {
|
||||
const arguments = ['-version'];
|
||||
final result = await process.run(
|
||||
executable,
|
||||
arguments,
|
||||
);
|
||||
|
||||
if (result.exitCode != ExitCode.success.code) {
|
||||
throw ProcessException(executable, arguments, '${result.stderr}');
|
||||
}
|
||||
|
||||
final lines = LineSplitter.split('${result.stdout}').map((e) => e.trim());
|
||||
var versionString = lines.firstOrNull?.split(' ').lastOrNull;
|
||||
if (versionString == null) {
|
||||
throw FormatException(
|
||||
'Could not parse Xcode version from output: "${result.stdout}".',
|
||||
);
|
||||
}
|
||||
|
||||
// [Version.parse] requires a patch number. If Xcode does not report a patch
|
||||
// number (e.g. "12.0"), add a patch number of 0 (e.g. "12.0.0")
|
||||
final noPachNumberRegex = RegExp(r'^\d+\.\d+$');
|
||||
if (noPachNumberRegex.hasMatch(versionString)) {
|
||||
versionString += '.0';
|
||||
}
|
||||
|
||||
return Version.parse(versionString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/executables/executables.dart';
|
||||
import 'package:shorebird_cli/src/process.dart';
|
||||
@@ -13,6 +14,7 @@ import '../mocks.dart';
|
||||
void main() {
|
||||
group(XcodeBuild, () {
|
||||
late ShorebirdProcess process;
|
||||
late ShorebirdProcessResult processResult;
|
||||
late XcodeBuild xcodeBuild;
|
||||
|
||||
R runWithOverrides<R>(R Function() body) {
|
||||
@@ -32,6 +34,7 @@ void main() {
|
||||
|
||||
setUp(() {
|
||||
process = MockShorebirdProcess();
|
||||
processResult = MockShorebirdProcessResult();
|
||||
xcodeBuild = runWithOverrides(XcodeBuild.new);
|
||||
});
|
||||
|
||||
@@ -132,5 +135,97 @@ To add iOS, run "flutter create . --platforms ios"''',
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('xcodeVersion', () {
|
||||
late ExitCode exitCode;
|
||||
late String stdout;
|
||||
|
||||
setUp(() {
|
||||
when(() => process.run(XcodeBuild.executable, ['-version']))
|
||||
.thenAnswer((_) async => processResult);
|
||||
when(() => processResult.exitCode).thenAnswer((_) => exitCode.code);
|
||||
when(() => processResult.stdout).thenAnswer((_) => stdout);
|
||||
});
|
||||
|
||||
group('when a non-zero exit code is returned', () {
|
||||
const errorMessage = 'An unexpected error occurred.';
|
||||
setUp(() {
|
||||
stdout = '';
|
||||
exitCode = ExitCode.cantCreate;
|
||||
when(() => processResult.stderr).thenReturn(errorMessage);
|
||||
});
|
||||
|
||||
test('throws a ProcessException', () async {
|
||||
expect(
|
||||
() => runWithOverrides(xcodeBuild.xcodeVersion),
|
||||
throwsA(
|
||||
isA<ProcessException>()
|
||||
.having((e) => e.message, 'message', errorMessage),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when stdout contains unexpected output', () {
|
||||
setUp(() {
|
||||
exitCode = ExitCode.success;
|
||||
});
|
||||
|
||||
test('throws FormatException if output is empty', () async {
|
||||
stdout = '';
|
||||
expect(
|
||||
() => runWithOverrides(xcodeBuild.xcodeVersion),
|
||||
throwsA(
|
||||
isA<FormatException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
'Could not parse Xcode version from output: "".',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('throws FormatException if output does not contain version',
|
||||
() async {
|
||||
stdout = 'unexpected output';
|
||||
expect(
|
||||
() => runWithOverrides(xcodeBuild.xcodeVersion),
|
||||
throwsA(
|
||||
isA<FormatException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
'Could not parse "output".',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when stdout contains valid output', () {
|
||||
setUp(() {
|
||||
exitCode = ExitCode.success;
|
||||
});
|
||||
|
||||
test('returns correct version with major, minor, and build numbers',
|
||||
() async {
|
||||
stdout = '''
|
||||
Xcode 14.3.1
|
||||
Build version 14E300c
|
||||
''';
|
||||
final version = await runWithOverrides(xcodeBuild.xcodeVersion);
|
||||
expect(version, Version(14, 3, 1));
|
||||
});
|
||||
|
||||
test('returns correct version with only major and minor numbers',
|
||||
() async {
|
||||
stdout = '''
|
||||
Xcode 15.0
|
||||
Build version 15A240d
|
||||
''';
|
||||
final version = await runWithOverrides(xcodeBuild.xcodeVersion);
|
||||
expect(version, Version(15, 0, 0));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user