refactor(shorebird_cli): add iOS artifact lookup methods to ArtifactManager (#2010)
This commit is contained in:
@@ -2,11 +2,14 @@ import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/cache.dart';
|
||||
import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
|
||||
/// A reference to a [ArtifactManager] instance.
|
||||
@@ -173,4 +176,88 @@ Failed to create diff (exit code ${result.exitCode}).
|
||||
|
||||
return archsDirectory.existsSync() ? archsDirectory : null;
|
||||
}
|
||||
|
||||
/// Returns the .xcarchive directory generated by `flutter build ipa`. This
|
||||
/// was traditionally named `Runner.xcarchive`, but can now be renamed.
|
||||
Directory? getXcarchiveDirectory() {
|
||||
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
|
||||
final archiveDirectory = Directory(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'archive',
|
||||
),
|
||||
);
|
||||
|
||||
if (!archiveDirectory.existsSync()) return null;
|
||||
|
||||
return archiveDirectory
|
||||
.listSync()
|
||||
.whereType<Directory>()
|
||||
.firstWhereOrNull((directory) => directory.path.endsWith('.xcarchive'));
|
||||
}
|
||||
|
||||
/// Returns the .app directory generated by `flutter build ipa`. This was
|
||||
/// traditionally named `Runner.app`, but can now be renamed.
|
||||
Directory? getIosAppDirectory({required Directory xcarchiveDirectory}) {
|
||||
final applicationsDirectory = Directory(
|
||||
p.join(
|
||||
xcarchiveDirectory.path,
|
||||
'Products',
|
||||
'Applications',
|
||||
),
|
||||
);
|
||||
|
||||
if (!applicationsDirectory.existsSync()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return applicationsDirectory
|
||||
.listSync()
|
||||
.whereType<Directory>()
|
||||
.firstWhereOrNull((directory) => directory.path.endsWith('.app'));
|
||||
}
|
||||
|
||||
/// Returns the path to the .ipa file generated by `flutter build ipa`.
|
||||
///
|
||||
/// Returns null if:
|
||||
/// - there is no ipa build directory (build/ios/ipa)
|
||||
/// - there is no .ipa file in the ipa build directory
|
||||
/// - there is more than one .ipa file in the ipa build directory
|
||||
File? getIpa() {
|
||||
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
|
||||
final ipaBuildDirectory = Directory(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'ipa',
|
||||
),
|
||||
);
|
||||
|
||||
if (!ipaBuildDirectory.existsSync()) {
|
||||
logger.detail('No directory found at ${ipaBuildDirectory.path}');
|
||||
return null;
|
||||
}
|
||||
|
||||
final ipaFiles = ipaBuildDirectory
|
||||
.listSync(recursive: true)
|
||||
.whereType<File>()
|
||||
.where((f) => p.extension(f.path) == '.ipa');
|
||||
|
||||
if (ipaFiles.isEmpty) {
|
||||
logger.detail('No .ipa files found in ${ipaBuildDirectory.path}');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ipaFiles.length > 1) {
|
||||
logger.detail(
|
||||
'More than one .ipa file found in ${ipaBuildDirectory.path}',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return ipaFiles.single;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,9 +512,7 @@ Either run `flutter pub get` manually, or follow the steps in ${link(uri: Uri.pa
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
exportOptionsPlist =
|
||||
File(p.join(tempDir.path, 'exportoptions.plist'));
|
||||
when(ios.createExportOptionsPlist).thenReturn(
|
||||
exportOptionsPlist,
|
||||
);
|
||||
when(ios.createExportOptionsPlist).thenReturn(exportOptionsPlist);
|
||||
});
|
||||
|
||||
group('with default arguments', () {
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/artifact_manager.dart';
|
||||
import 'package:shorebird_cli/src/cache.dart';
|
||||
import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
@@ -19,6 +21,9 @@ void main() {
|
||||
late Cache cache;
|
||||
late Directory cacheArtifactDirectory;
|
||||
late http.Client httpClient;
|
||||
late Directory projectRoot;
|
||||
late Logger logger;
|
||||
late ShorebirdEnv shorebirdEnv;
|
||||
late ShorebirdProcessResult patchProcessResult;
|
||||
late ShorebirdProcess shorebirdProcess;
|
||||
late ArtifactManager artifactManager;
|
||||
@@ -29,7 +34,9 @@ void main() {
|
||||
values: {
|
||||
cacheRef.overrideWith(() => cache),
|
||||
httpClientRef.overrideWith(() => httpClient),
|
||||
loggerRef.overrideWith(() => logger),
|
||||
processRef.overrideWith(() => shorebirdProcess),
|
||||
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -42,9 +49,11 @@ void main() {
|
||||
cacheArtifactDirectory = Directory.systemTemp.createTempSync();
|
||||
cache = MockCache();
|
||||
httpClient = MockHttpClient();
|
||||
logger = MockLogger();
|
||||
patchProcessResult = MockProcessResult();
|
||||
projectRoot = Directory.systemTemp.createTempSync();
|
||||
shorebirdProcess = MockShorebirdProcess();
|
||||
artifactManager = ArtifactManager();
|
||||
shorebirdEnv = MockShorebirdEnv();
|
||||
|
||||
when(() => cache.getArtifactDirectory(any()))
|
||||
.thenReturn(cacheArtifactDirectory);
|
||||
@@ -53,6 +62,10 @@ void main() {
|
||||
when(() => httpClient.send(any())).thenAnswer(
|
||||
(_) async => http.StreamedResponse(const Stream.empty(), HttpStatus.ok),
|
||||
);
|
||||
|
||||
when(() => shorebirdEnv.getShorebirdProjectRoot())
|
||||
.thenReturn(projectRoot);
|
||||
|
||||
when(
|
||||
() => shorebirdProcess.run(
|
||||
any(that: endsWith('patch')),
|
||||
@@ -68,6 +81,8 @@ void main() {
|
||||
return patchProcessResult;
|
||||
});
|
||||
when(() => patchProcessResult.exitCode).thenReturn(ExitCode.success.code);
|
||||
|
||||
artifactManager = ArtifactManager();
|
||||
});
|
||||
|
||||
group('createDiff', () {
|
||||
@@ -399,5 +414,150 @@ void main() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('getXcarchiveDirectory', () {
|
||||
group('when archive directory exists', () {
|
||||
late Directory archiveDirectory;
|
||||
|
||||
setUp(() {
|
||||
archiveDirectory = Directory(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'archive',
|
||||
'Runner.xcarchive',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
});
|
||||
|
||||
test('returns path to archive directory', () async {
|
||||
final result = runWithOverrides(
|
||||
() => artifactManager.getXcarchiveDirectory(),
|
||||
);
|
||||
|
||||
expect(result, isNotNull);
|
||||
expect(result!.path, equals(archiveDirectory.path));
|
||||
});
|
||||
});
|
||||
|
||||
group('when archive directory does not exist', () {
|
||||
test('returns null', () {
|
||||
expect(
|
||||
runWithOverrides(artifactManager.getXcarchiveDirectory),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('getIosAppDirectory', () {
|
||||
group('when applications directory does not exist', () {
|
||||
test('returns null', () {
|
||||
final xcarchiveDirectory = Directory.systemTemp.createTempSync();
|
||||
expect(
|
||||
runWithOverrides(
|
||||
() => artifactManager.getIosAppDirectory(
|
||||
xcarchiveDirectory: xcarchiveDirectory,
|
||||
),
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when applications directory exists', () {
|
||||
late Directory applicationsDirectory;
|
||||
late Directory xcarchiveDirectory;
|
||||
|
||||
setUp(() {
|
||||
xcarchiveDirectory = Directory.systemTemp.createTempSync();
|
||||
applicationsDirectory = Directory(
|
||||
p.join(
|
||||
xcarchiveDirectory.path,
|
||||
'Products',
|
||||
'Applications',
|
||||
'Runner.app',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
});
|
||||
|
||||
test('returns path to applications directory', () {
|
||||
final result = runWithOverrides(
|
||||
() => artifactManager.getIosAppDirectory(
|
||||
xcarchiveDirectory: xcarchiveDirectory,
|
||||
),
|
||||
);
|
||||
|
||||
expect(result, isNotNull);
|
||||
expect(result!.path, equals(applicationsDirectory.path));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('getIpa', () {
|
||||
group('when ipa build directory does not exist', () {
|
||||
test('returns null', () {
|
||||
expect(
|
||||
runWithOverrides(artifactManager.getIpa),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when ipa build directory exists', () {
|
||||
late Directory ipaBuildDirectory;
|
||||
late File ipaFile;
|
||||
|
||||
setUp(() {
|
||||
ipaBuildDirectory = Directory(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'ipa',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
ipaFile = File(p.join(ipaBuildDirectory.path, 'Runner.ipa'))
|
||||
..createSync();
|
||||
});
|
||||
|
||||
test('returns path to ipa file', () {
|
||||
final result = runWithOverrides(artifactManager.getIpa);
|
||||
|
||||
expect(result, isNotNull);
|
||||
expect(result!.path, equals(ipaFile.path));
|
||||
});
|
||||
|
||||
test('returns null when multiple ipa files exist', () {
|
||||
File(p.join(ipaBuildDirectory.path, 'Runner2.ipa')).createSync();
|
||||
|
||||
expect(
|
||||
runWithOverrides(artifactManager.getIpa),
|
||||
isNull,
|
||||
);
|
||||
verify(
|
||||
() => logger.detail(
|
||||
'More than one .ipa file found in ${ipaBuildDirectory.path}',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('returns null when no ipa files exist', () {
|
||||
ipaFile.deleteSync();
|
||||
|
||||
expect(
|
||||
runWithOverrides(artifactManager.getIpa),
|
||||
isNull,
|
||||
);
|
||||
|
||||
verify(
|
||||
() => logger.detail(
|
||||
'No .ipa files found in ${ipaBuildDirectory.path}',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user