feat(shorebird_cli): iOS linking improvment (improved class table sort) (#2655)
This commit is contained in:
@@ -1 +1 @@
|
||||
e8206cf2e76e3a4e0c8e062c20bb563cae39a1f1
|
||||
5a3ba347fd4e2ed6b17077af8ea2aca58bb26747
|
||||
|
||||
@@ -7,9 +7,10 @@ import 'package:path/path.dart' as p;
|
||||
/// A wrapper around a directory that can be zipped.
|
||||
extension DirectoryArchive on Directory {
|
||||
/// Copies this directory to a temporary directory and zips it.
|
||||
Future<File> zipToTempFile() async {
|
||||
Future<File> zipToTempFile({String? name}) async {
|
||||
final tempDir = await Directory.systemTemp.createTemp();
|
||||
final outFile = File(p.join(tempDir.path, '${p.basename(path)}.zip'));
|
||||
final fileName = name ?? p.basename(path);
|
||||
final outFile = File(p.join(tempDir.path, '$fileName.zip'));
|
||||
await Isolate.run(() {
|
||||
ZipFileEncoder().zipDirectory(this, filename: outFile.path);
|
||||
});
|
||||
|
||||
@@ -357,6 +357,26 @@ class ArtifactManager {
|
||||
return ipaFiles.single;
|
||||
}
|
||||
|
||||
/// Returns the path to the shorebird release supplement directory on iOS.
|
||||
///
|
||||
/// Returns null if there is no supplement directory
|
||||
/// (e.g. when using older Flutter revisions).
|
||||
Directory? getIosReleaseSupplementDirectory() {
|
||||
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
|
||||
final releaseSupplementDir = Directory(
|
||||
p.join(projectRoot.path, 'build', 'ios', 'shorebird'),
|
||||
);
|
||||
|
||||
if (!releaseSupplementDir.existsSync()) {
|
||||
logger.detail(
|
||||
'No iOS release supplements found at ${releaseSupplementDir.path}',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return releaseSupplementDir;
|
||||
}
|
||||
|
||||
/// Name of the App.xcframework generated by `shorebird release ios-framework`
|
||||
static const String appXcframeworkName = 'App.xcframework';
|
||||
|
||||
|
||||
@@ -616,7 +616,8 @@ aar artifact already exists, continuing...''',
|
||||
return thinnedArchiveDirectory;
|
||||
}
|
||||
|
||||
/// Uploads a release .xcarchive and .app to the Shorebird server.
|
||||
/// Uploads a release .xcarchive, .app, and supplementary files to the
|
||||
/// Shorebird server.
|
||||
Future<void> createIosReleaseArtifacts({
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
@@ -624,6 +625,7 @@ aar artifact already exists, continuing...''',
|
||||
required String runnerPath,
|
||||
required bool isCodesigned,
|
||||
required String? podfileLockHash,
|
||||
required String? supplementPath,
|
||||
}) async {
|
||||
final createArtifactProgress = logger.progress('Uploading artifacts');
|
||||
final thinnedArchiveDirectory =
|
||||
@@ -668,14 +670,40 @@ aar artifact already exists, continuing...''',
|
||||
);
|
||||
}
|
||||
|
||||
if (supplementPath != null) {
|
||||
final zippedSupplement = await Directory(supplementPath).zipToTempFile(
|
||||
name: 'ios_supplement',
|
||||
);
|
||||
try {
|
||||
await codePushClient.createReleaseArtifact(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
artifactPath: zippedSupplement.path,
|
||||
arch: 'ios_supplement',
|
||||
platform: ReleasePlatform.ios,
|
||||
hash: sha256.convert(await zippedSupplement.readAsBytes()).toString(),
|
||||
canSideload: false,
|
||||
podfileLockHash: podfileLockHash,
|
||||
);
|
||||
} catch (error) {
|
||||
_handleErrorAndExit(
|
||||
error,
|
||||
progress: createArtifactProgress,
|
||||
message: 'Error uploading release supplements: $error',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createArtifactProgress.complete();
|
||||
}
|
||||
|
||||
/// Zips and uploads a release xcframework to the Shorebird server.
|
||||
/// Zips and uploads a release xcframework and supplementary files to the
|
||||
/// Shorebird server.
|
||||
Future<void> createIosFrameworkReleaseArtifacts({
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required String appFrameworkPath,
|
||||
required String? supplementPath,
|
||||
}) async {
|
||||
final createArtifactProgress = logger.progress('Uploading artifacts');
|
||||
final appFrameworkDirectory = Directory(appFrameworkPath);
|
||||
@@ -705,6 +733,30 @@ aar artifact already exists, continuing...''',
|
||||
);
|
||||
}
|
||||
|
||||
if (supplementPath != null) {
|
||||
final zippedSupplement = await Directory(supplementPath).zipToTempFile(
|
||||
name: 'ios_framework_supplement',
|
||||
);
|
||||
try {
|
||||
await codePushClient.createReleaseArtifact(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
artifactPath: zippedSupplement.path,
|
||||
arch: 'ios_framework_supplement',
|
||||
platform: ReleasePlatform.ios,
|
||||
hash: sha256.convert(await zippedSupplement.readAsBytes()).toString(),
|
||||
canSideload: false,
|
||||
podfileLockHash: null,
|
||||
);
|
||||
} catch (error) {
|
||||
_handleErrorAndExit(
|
||||
error,
|
||||
progress: createArtifactProgress,
|
||||
message: 'Error uploading release supplements: $error',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createArtifactProgress.complete();
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ class AarPatcher extends Patcher {
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required File releaseArtifact,
|
||||
File? supplementArtifact,
|
||||
}) async {
|
||||
final releaseArtifacts = await codePushClientWrapper.getReleaseArtifacts(
|
||||
appId: appId,
|
||||
|
||||
@@ -138,6 +138,7 @@ Looked in:
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required File releaseArtifact,
|
||||
File? supplementArtifact,
|
||||
Duration downloadMessageTimeout = const Duration(minutes: 1),
|
||||
}) async {
|
||||
final releaseArtifacts = await codePushClientWrapper.getReleaseArtifacts(
|
||||
|
||||
@@ -39,6 +39,9 @@ class IosFrameworkPatcher extends Patcher {
|
||||
required super.target,
|
||||
});
|
||||
|
||||
String get _patchClassTableLinkInfoFile =>
|
||||
p.join(buildDirectory.path, 'ios', 'shorebird', 'App.ct.link');
|
||||
|
||||
String get _vmcodeOutputPath => p.join(buildDirectory.path, 'out.vmcode');
|
||||
|
||||
String get _appDillCopyPath => p.join(buildDirectory.path, 'app.dill');
|
||||
@@ -46,6 +49,9 @@ class IosFrameworkPatcher extends Patcher {
|
||||
@override
|
||||
String get primaryReleaseArtifactArch => 'xcframework';
|
||||
|
||||
@override
|
||||
String? get supplementaryReleaseArtifactArch => 'ios_framework_supplement';
|
||||
|
||||
@override
|
||||
ReleaseType get releaseType => ReleaseType.iosFramework;
|
||||
|
||||
@@ -145,17 +151,37 @@ class IosFrameworkPatcher extends Patcher {
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required File releaseArtifact,
|
||||
File? supplementArtifact,
|
||||
}) async {
|
||||
final unzipProgress = logger.progress('Extracting release artifact');
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
await artifactManager.extractZip(
|
||||
zipFile: releaseArtifact,
|
||||
outputDirectory: tempDir,
|
||||
);
|
||||
final releaseXcframeworkPath = tempDir.path;
|
||||
late final String releaseXcframeworkPath;
|
||||
{
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
await artifactManager.extractZip(
|
||||
zipFile: releaseArtifact,
|
||||
outputDirectory: tempDir,
|
||||
);
|
||||
releaseXcframeworkPath = tempDir.path;
|
||||
}
|
||||
|
||||
unzipProgress
|
||||
.complete('Extracted release artifact to $releaseXcframeworkPath');
|
||||
File? releaseClassTableLinkInfoFile;
|
||||
if (supplementArtifact != null) {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
await artifactManager.extractZip(
|
||||
zipFile: supplementArtifact,
|
||||
outputDirectory: tempDir,
|
||||
);
|
||||
releaseClassTableLinkInfoFile = File(p.join(tempDir.path, 'App.ct.link'));
|
||||
|
||||
if (!releaseClassTableLinkInfoFile.existsSync()) {
|
||||
logger.err('Unable to find class table link info file');
|
||||
throw ProcessExit(ExitCode.software.code);
|
||||
}
|
||||
}
|
||||
|
||||
unzipProgress.complete(
|
||||
'Extracted release artifact to $releaseXcframeworkPath',
|
||||
);
|
||||
final releaseArtifactFile = File(
|
||||
p.join(
|
||||
releaseXcframeworkPath,
|
||||
@@ -174,6 +200,22 @@ class IosFrameworkPatcher extends Patcher {
|
||||
);
|
||||
final useLinker = AotTools.usesLinker(shorebirdEnv.flutterRevision);
|
||||
if (useLinker) {
|
||||
// If we're using a newer version of the linker, we need to also copy the
|
||||
// necessary class table link information alongside the snapshots.
|
||||
if (releaseClassTableLinkInfoFile != null) {
|
||||
// Copy the release's class table link info file next to the release
|
||||
// snapshot so that it can be used to generate a patch.
|
||||
releaseClassTableLinkInfoFile.copySync(
|
||||
p.join(releaseArtifactFile.parent.path, 'App.ct.link'),
|
||||
);
|
||||
|
||||
// Copy the patch's class table link info file to the build directory
|
||||
// so that it can be used to generate a patch.
|
||||
File(_patchClassTableLinkInfoFile).copySync(
|
||||
p.join(buildDirectory.path, 'out.ct.link'),
|
||||
);
|
||||
}
|
||||
|
||||
await _runLinker(
|
||||
aotSnapshot: aotSnapshotFile,
|
||||
releaseArtifact: releaseArtifactFile,
|
||||
|
||||
@@ -47,6 +47,9 @@ class IosPatcher extends Patcher {
|
||||
required super.target,
|
||||
});
|
||||
|
||||
String get _patchClassTableLinkInfoPath =>
|
||||
p.join(buildDirectory.path, 'ios', 'shorebird', 'App.ct.link');
|
||||
|
||||
String get _aotOutputPath => p.join(buildDirectory.path, 'out.aot');
|
||||
|
||||
String get _vmcodeOutputPath => p.join(buildDirectory.path, 'out.vmcode');
|
||||
@@ -85,6 +88,9 @@ class IosPatcher extends Patcher {
|
||||
@override
|
||||
String get primaryReleaseArtifactArch => 'xcarchive';
|
||||
|
||||
@override
|
||||
String? get supplementaryReleaseArtifactArch => 'ios_supplement';
|
||||
|
||||
@override
|
||||
Future<void> assertPreconditions() async {
|
||||
try {
|
||||
@@ -229,6 +235,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required File releaseArtifact,
|
||||
File? supplementArtifact,
|
||||
}) async {
|
||||
// Verify that we have built a patch .xcarchive
|
||||
if (artifactManager.getXcarchiveDirectory()?.path == null) {
|
||||
@@ -237,12 +244,31 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
}
|
||||
|
||||
final unzipProgress = logger.progress('Extracting release artifact');
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
await artifactManager.extractZip(
|
||||
zipFile: releaseArtifact,
|
||||
outputDirectory: tempDir,
|
||||
);
|
||||
final releaseXcarchivePath = tempDir.path;
|
||||
|
||||
late final String releaseXcarchivePath;
|
||||
{
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
await artifactManager.extractZip(
|
||||
zipFile: releaseArtifact,
|
||||
outputDirectory: tempDir,
|
||||
);
|
||||
releaseXcarchivePath = tempDir.path;
|
||||
}
|
||||
|
||||
File? releaseClassTableLinkInfoFile;
|
||||
if (supplementArtifact != null) {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
await artifactManager.extractZip(
|
||||
zipFile: supplementArtifact,
|
||||
outputDirectory: tempDir,
|
||||
);
|
||||
releaseClassTableLinkInfoFile = File(p.join(tempDir.path, 'App.ct.link'));
|
||||
|
||||
if (!releaseClassTableLinkInfoFile.existsSync()) {
|
||||
logger.err('Unable to find class table link info file');
|
||||
throw ProcessExit(ExitCode.software.code);
|
||||
}
|
||||
}
|
||||
|
||||
unzipProgress.complete();
|
||||
final appDirectory = artifactManager.getIosAppDirectory(
|
||||
@@ -263,6 +289,22 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
|
||||
final useLinker = AotTools.usesLinker(shorebirdEnv.flutterRevision);
|
||||
if (useLinker) {
|
||||
// If we're using a newer version of the linker, we need to also copy the
|
||||
// necessary class table link information alongside the snapshots.
|
||||
if (releaseClassTableLinkInfoFile != null) {
|
||||
// Copy the release's class table link info file next to the release
|
||||
// snapshot so that it can be used to generate a patch.
|
||||
releaseClassTableLinkInfoFile.copySync(
|
||||
p.join(releaseArtifactFile.parent.path, 'App.ct.link'),
|
||||
);
|
||||
|
||||
// Copy the patch's class table link info file to the build directory
|
||||
// so that it can be used to generate a patch.
|
||||
File(_patchClassTableLinkInfoPath).copySync(
|
||||
p.join(buildDirectory.path, 'out.ct.link'),
|
||||
);
|
||||
}
|
||||
|
||||
final (:exitCode, :linkPercentage) = await _runLinker(
|
||||
releaseArtifact: releaseArtifactFile,
|
||||
kernelFile: File(_appDillCopyPath),
|
||||
|
||||
@@ -283,11 +283,24 @@ NOTE: this is ${styleBold.wrap('not')} recommended. Asset changes cannot be incl
|
||||
platform: patcher.releaseType.releasePlatform,
|
||||
);
|
||||
|
||||
final releaseArchive = await downloadPrimaryReleaseArtifact(
|
||||
final supplementalArtifact =
|
||||
patcher.supplementaryReleaseArtifactArch != null
|
||||
? await codePushClientWrapper.getOptionalReleaseArtifact(
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
arch: patcher.supplementaryReleaseArtifactArch!,
|
||||
platform: patcher.releaseType.releasePlatform,
|
||||
)
|
||||
: null;
|
||||
|
||||
final releaseArchive = await downloadReleaseArtifact(
|
||||
releaseArtifact: releaseArtifact,
|
||||
patcher: patcher,
|
||||
);
|
||||
|
||||
final supplementArchive = supplementalArtifact != null
|
||||
? await downloadReleaseArtifact(releaseArtifact: supplementalArtifact)
|
||||
: null;
|
||||
|
||||
final releaseFlutterShorebirdEnv = shorebirdEnv.copyWith(
|
||||
flutterRevisionOverride: release.flutterRevision,
|
||||
);
|
||||
@@ -313,6 +326,7 @@ NOTE: this is ${styleBold.wrap('not')} recommended. Asset changes cannot be incl
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
releaseArtifact: releaseArchive,
|
||||
supplementArtifact: supplementArchive,
|
||||
);
|
||||
|
||||
final dryRun = results['dry-run'] == true;
|
||||
@@ -478,15 +492,14 @@ ${summary.join('\n')}
|
||||
}
|
||||
}
|
||||
|
||||
Future<File> downloadPrimaryReleaseArtifact({
|
||||
Future<File> downloadReleaseArtifact({
|
||||
required ReleaseArtifact releaseArtifact,
|
||||
required Patcher patcher,
|
||||
}) async {
|
||||
final File artifactFile;
|
||||
try {
|
||||
artifactFile = await artifactManager.downloadWithProgressUpdates(
|
||||
Uri.parse(releaseArtifact.url),
|
||||
message: 'Downloading ${patcher.primaryReleaseArtifactArch}',
|
||||
message: 'Downloading ${releaseArtifact.arch}',
|
||||
);
|
||||
} catch (_) {
|
||||
throw ProcessExit(ExitCode.software.code);
|
||||
@@ -495,3 +508,24 @@ ${summary.join('\n')}
|
||||
return artifactFile;
|
||||
}
|
||||
}
|
||||
|
||||
extension on CodePushClientWrapper {
|
||||
Future<ReleaseArtifact?> getOptionalReleaseArtifact({
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required String arch,
|
||||
required ReleasePlatform platform,
|
||||
}) async {
|
||||
try {
|
||||
final artifact = await getReleaseArtifact(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
arch: arch,
|
||||
platform: platform,
|
||||
);
|
||||
return artifact;
|
||||
} on CodePushNotFoundException catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ ${iOSLinkPercentageUrl.toLink()}
|
||||
/// For example, 'aab' for Android, 'xcarchive' for iOS.
|
||||
String get primaryReleaseArtifactArch;
|
||||
|
||||
/// The identifier used for any supplementary release artifacts.
|
||||
String? get supplementaryReleaseArtifactArch => null;
|
||||
|
||||
/// The root directory of the current project.
|
||||
Directory get projectRoot => shorebirdEnv.getShorebirdProjectRoot()!;
|
||||
|
||||
@@ -91,6 +94,7 @@ ${iOSLinkPercentageUrl.toLink()}
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required File releaseArtifact,
|
||||
File? supplementArtifact,
|
||||
});
|
||||
|
||||
/// Updates the provided metadata to include patcher-specific fields.
|
||||
|
||||
@@ -144,6 +144,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
appFrameworkPath: p.join(releaseDirectory.path, 'App.xcframework'),
|
||||
supplementPath: artifactManager.getIosReleaseSupplementDirectory()?.path,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
.path,
|
||||
isCodesigned: codesign,
|
||||
podfileLockHash: podfileLockHash,
|
||||
supplementPath: artifactManager.getIosReleaseSupplementDirectory()?.path,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -806,5 +806,45 @@ void main() {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('getIosReleaseSupplementDirectory', () {
|
||||
group('when the directory does not exist', () {
|
||||
test('returns null', () {
|
||||
expect(
|
||||
runWithOverrides(artifactManager.getIosReleaseSupplementDirectory),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when the directory exists', () {
|
||||
setUp(() {
|
||||
Directory(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'shorebird',
|
||||
),
|
||||
).createSync(recursive: true);
|
||||
});
|
||||
|
||||
test('returns path to the directory', () {
|
||||
expect(
|
||||
runWithOverrides(
|
||||
artifactManager.getIosReleaseSupplementDirectory,
|
||||
)?.path,
|
||||
equals(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'shorebird',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1709,6 +1709,7 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
const podfileLockHash = 'podfile-lock-hash';
|
||||
final xcarchivePath = p.join('path', 'to', 'app.xcarchive');
|
||||
final runnerPath = p.join('path', 'to', 'runner.app');
|
||||
final releaseSupplementPath = p.join('path', 'to', 'supplement');
|
||||
|
||||
void setUpProjectRoot({String? flavor}) {
|
||||
Directory(
|
||||
@@ -1717,6 +1718,9 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
Directory(
|
||||
p.join(projectRoot.path, runnerPath),
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
p.join(projectRoot.path, releaseSupplementPath),
|
||||
).createSync(recursive: true);
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
@@ -1761,6 +1765,7 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
runnerPath: p.join(projectRoot.path, runnerPath),
|
||||
isCodesigned: true,
|
||||
podfileLockHash: podfileLockHash,
|
||||
supplementPath: p.join(projectRoot.path, releaseSupplementPath),
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
@@ -1798,6 +1803,7 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
runnerPath: p.join(projectRoot.path, runnerPath),
|
||||
isCodesigned: false,
|
||||
podfileLockHash: podfileLockHash,
|
||||
supplementPath: p.join(projectRoot.path, releaseSupplementPath),
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
@@ -1835,6 +1841,45 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
runnerPath: p.join(projectRoot.path, runnerPath),
|
||||
isCodesigned: false,
|
||||
podfileLockHash: podfileLockHash,
|
||||
supplementPath: p.join(projectRoot.path, releaseSupplementPath),
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
);
|
||||
|
||||
verify(() => progress.fail(any(that: contains(error)))).called(1);
|
||||
});
|
||||
|
||||
test('exits with code 70 when supplement artifact creation fails',
|
||||
() async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.createReleaseArtifact(
|
||||
appId: any(named: 'appId'),
|
||||
artifactPath: any(
|
||||
named: 'artifactPath',
|
||||
that: endsWith('ios_supplement.zip'),
|
||||
),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
arch: any(named: 'arch'),
|
||||
platform: any(named: 'platform'),
|
||||
hash: any(named: 'hash'),
|
||||
canSideload: any(named: 'canSideload'),
|
||||
podfileLockHash: any(named: 'podfileLockHash'),
|
||||
),
|
||||
).thenThrow(error);
|
||||
setUpProjectRoot();
|
||||
|
||||
await expectLater(
|
||||
() async => runWithOverrides(
|
||||
() async => codePushClientWrapper.createIosReleaseArtifacts(
|
||||
appId: app.appId,
|
||||
releaseId: releaseId,
|
||||
xcarchivePath: p.join(projectRoot.path, xcarchivePath),
|
||||
runnerPath: p.join(projectRoot.path, runnerPath),
|
||||
isCodesigned: false,
|
||||
podfileLockHash: podfileLockHash,
|
||||
supplementPath: p.join(projectRoot.path, releaseSupplementPath),
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
@@ -1866,6 +1911,7 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
runnerPath: p.join(projectRoot.path, runnerPath),
|
||||
isCodesigned: true,
|
||||
podfileLockHash: podfileLockHash,
|
||||
supplementPath: p.join(projectRoot.path, releaseSupplementPath),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1891,44 +1937,35 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
|
||||
group('createIosFrameworkReleaseArtifacts', () {
|
||||
final frameworkPath = p.join('path', 'to', 'App.xcframework');
|
||||
final releaseSupplementPath = p.join('path', 'to', 'supplement');
|
||||
|
||||
void setUpProjectRoot({String? flavor}) {
|
||||
Directory(
|
||||
p.join(projectRoot.path, frameworkPath),
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
p.join(projectRoot.path, releaseSupplementPath),
|
||||
).createSync(recursive: true);
|
||||
}
|
||||
|
||||
test(
|
||||
'exits with code 70 when creating xcframework artifact fails',
|
||||
() async {
|
||||
when(
|
||||
() => codePushClient.createReleaseArtifact(
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
appId: any(named: 'appId'),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
arch: any(named: 'arch'),
|
||||
platform: any(named: 'platform'),
|
||||
hash: any(named: 'hash'),
|
||||
canSideload: any(named: 'canSideload'),
|
||||
podfileLockHash: any(named: 'podfileLockHash'),
|
||||
),
|
||||
).thenThrow(Exception('oh no'));
|
||||
setUpProjectRoot();
|
||||
setUp(() {
|
||||
when(
|
||||
() => codePushClient.createReleaseArtifact(
|
||||
appId: any(named: 'appId'),
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
arch: any(named: 'arch'),
|
||||
platform: any(named: 'platform'),
|
||||
hash: any(named: 'hash'),
|
||||
canSideload: any(named: 'canSideload'),
|
||||
podfileLockHash: any(named: 'podfileLockHash'),
|
||||
),
|
||||
).thenAnswer((_) async {});
|
||||
setUpProjectRoot();
|
||||
});
|
||||
|
||||
await expectLater(
|
||||
() async => runWithOverrides(
|
||||
() => codePushClientWrapper.createIosFrameworkReleaseArtifacts(
|
||||
appId: app.appId,
|
||||
releaseId: releaseId,
|
||||
appFrameworkPath: p.join(projectRoot.path, frameworkPath),
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('completes successfully when release artifact is created', () async {
|
||||
test('exits with code 70 when creating xcframework artifact fails',
|
||||
() async {
|
||||
when(
|
||||
() => codePushClient.createReleaseArtifact(
|
||||
artifactPath: any(named: 'artifactPath'),
|
||||
@@ -1940,15 +1977,64 @@ You can manage this release in the ${link(uri: uri, message: 'Shorebird Console'
|
||||
canSideload: any(named: 'canSideload'),
|
||||
podfileLockHash: any(named: 'podfileLockHash'),
|
||||
),
|
||||
).thenAnswer((_) async {});
|
||||
setUpProjectRoot();
|
||||
).thenThrow(Exception('oh no'));
|
||||
|
||||
await expectLater(
|
||||
() async => runWithOverrides(
|
||||
() => codePushClientWrapper.createIosFrameworkReleaseArtifacts(
|
||||
appId: app.appId,
|
||||
releaseId: releaseId,
|
||||
appFrameworkPath: p.join(projectRoot.path, frameworkPath),
|
||||
supplementPath: null,
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
);
|
||||
});
|
||||
|
||||
test('exits with code 70 when supplement artifact creation fails',
|
||||
() async {
|
||||
const error = 'something went wrong';
|
||||
when(
|
||||
() => codePushClient.createReleaseArtifact(
|
||||
appId: any(named: 'appId'),
|
||||
artifactPath: any(
|
||||
named: 'artifactPath',
|
||||
that: endsWith('ios_framework_supplement.zip'),
|
||||
),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
arch: any(named: 'arch'),
|
||||
platform: any(named: 'platform'),
|
||||
hash: any(named: 'hash'),
|
||||
canSideload: any(named: 'canSideload'),
|
||||
podfileLockHash: any(named: 'podfileLockHash'),
|
||||
),
|
||||
).thenThrow(error);
|
||||
|
||||
await expectLater(
|
||||
() async => runWithOverrides(
|
||||
() async =>
|
||||
codePushClientWrapper.createIosFrameworkReleaseArtifacts(
|
||||
appId: app.appId,
|
||||
releaseId: releaseId,
|
||||
appFrameworkPath: p.join(projectRoot.path, frameworkPath),
|
||||
supplementPath: p.join(projectRoot.path, releaseSupplementPath),
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
);
|
||||
|
||||
verify(() => progress.fail(any(that: contains(error)))).called(1);
|
||||
});
|
||||
|
||||
test('completes successfully when release artifact is created', () async {
|
||||
await expectLater(
|
||||
runWithOverrides(
|
||||
() => codePushClientWrapper.createIosFrameworkReleaseArtifacts(
|
||||
appId: app.appId,
|
||||
releaseId: releaseId,
|
||||
appFrameworkPath: p.join(projectRoot.path, frameworkPath),
|
||||
supplementPath: null,
|
||||
),
|
||||
),
|
||||
completes,
|
||||
|
||||
@@ -143,6 +143,15 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('supplementaryReleaseArtifactArch', () {
|
||||
test('is "ios_framework_supplement"', () {
|
||||
expect(
|
||||
patcher.supplementaryReleaseArtifactArch,
|
||||
'ios_framework_supplement',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('releaseType', () {
|
||||
test('is ReleaseType.iosFramework', () {
|
||||
expect(patcher.releaseType, ReleaseType.iosFramework);
|
||||
@@ -499,13 +508,14 @@ void main() {
|
||||
canSideload: true,
|
||||
);
|
||||
late File releaseArtifactFile;
|
||||
late File supplementArtifactFile;
|
||||
|
||||
void setUpProjectRootArtifacts() {
|
||||
File(p.join(projectRoot.path, 'build', elfAotSnapshotFileName))
|
||||
.createSync(
|
||||
recursive: true,
|
||||
);
|
||||
Directory(
|
||||
File(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
@@ -513,13 +523,23 @@ void main() {
|
||||
'framework',
|
||||
'Release',
|
||||
'App.xcframework',
|
||||
'ios-arm64',
|
||||
'App.framework',
|
||||
'App',
|
||||
),
|
||||
).createSync(
|
||||
recursive: true,
|
||||
);
|
||||
).createSync(recursive: true);
|
||||
File(
|
||||
p.join(projectRoot.path, 'build', linkFileName),
|
||||
).createSync(recursive: true);
|
||||
File(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'shorebird',
|
||||
'App.ct.link',
|
||||
),
|
||||
).createSync(recursive: true);
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
@@ -529,6 +549,12 @@ void main() {
|
||||
'release.xcframework',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
supplementArtifactFile = File(
|
||||
p.join(
|
||||
Directory.systemTemp.createTempSync().path,
|
||||
'ios_framework_supplement.zip',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
|
||||
when(
|
||||
() => codePushClientWrapper.getReleaseArtifact(
|
||||
@@ -828,6 +854,92 @@ void main() {
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
group('when class table link info is not present', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => artifactManager.extractZip(
|
||||
zipFile: supplementArtifactFile,
|
||||
outputDirectory: any(named: 'outputDirectory'),
|
||||
),
|
||||
).thenAnswer((invocation) async {});
|
||||
});
|
||||
|
||||
test('exits with code 70', () async {
|
||||
await expectLater(
|
||||
() => runWithOverrides(
|
||||
() => patcher.createPatchArtifacts(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
releaseArtifact: releaseArtifactFile,
|
||||
supplementArtifact: supplementArtifactFile,
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => logger.err(
|
||||
'Unable to find class table link info file',
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('when class table link info is present', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => artifactManager.extractZip(
|
||||
zipFile: releaseArtifactFile,
|
||||
outputDirectory: any(named: 'outputDirectory'),
|
||||
),
|
||||
).thenAnswer((invocation) async {
|
||||
final outDir = invocation.namedArguments[#outputDirectory]
|
||||
as Directory;
|
||||
File(
|
||||
p.join(
|
||||
outDir.path,
|
||||
'ios-arm64',
|
||||
'App.framework',
|
||||
'App',
|
||||
),
|
||||
).createSync(recursive: true);
|
||||
});
|
||||
when(
|
||||
() => artifactManager.extractZip(
|
||||
zipFile: supplementArtifactFile,
|
||||
outputDirectory: any(named: 'outputDirectory'),
|
||||
),
|
||||
).thenAnswer((invocation) async {
|
||||
final outDir = invocation.namedArguments[#outputDirectory]
|
||||
as Directory;
|
||||
File(
|
||||
p.join(outDir.path, 'App.ct.link'),
|
||||
).createSync(recursive: true);
|
||||
});
|
||||
});
|
||||
|
||||
test('returns linked patch artifact in patch bundle', () async {
|
||||
final patchBundle = await runWithOverrides(
|
||||
() => patcher.createPatchArtifacts(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
releaseArtifact: releaseArtifactFile,
|
||||
supplementArtifact: supplementArtifactFile,
|
||||
),
|
||||
);
|
||||
|
||||
expect(patchBundle, hasLength(1));
|
||||
expect(
|
||||
patchBundle[Arch.arm64],
|
||||
isA<PatchArtifactBundle>().having(
|
||||
(b) => b.path,
|
||||
'path',
|
||||
endsWith(diffPath),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -158,6 +158,12 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('supplementaryReleaseArtifactArch', () {
|
||||
test('is "ios_supplement"', () {
|
||||
expect(patcher.supplementaryReleaseArtifactArch, 'ios_supplement');
|
||||
});
|
||||
});
|
||||
|
||||
group('releaseType', () {
|
||||
test('is ReleaseType.ios', () {
|
||||
expect(patcher.releaseType, ReleaseType.ios);
|
||||
@@ -818,12 +824,12 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
canSideload: true,
|
||||
);
|
||||
late File releaseArtifactFile;
|
||||
late File supplementArtifactFile;
|
||||
|
||||
void setUpProjectRootArtifacts() {
|
||||
File(p.join(projectRoot.path, 'build', elfAotSnapshotFileName))
|
||||
.createSync(
|
||||
recursive: true,
|
||||
);
|
||||
File(
|
||||
p.join(projectRoot.path, 'build', elfAotSnapshotFileName),
|
||||
).createSync(recursive: true);
|
||||
Directory(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
@@ -833,10 +839,8 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
'Release',
|
||||
'App.xcframework',
|
||||
),
|
||||
).createSync(
|
||||
recursive: true,
|
||||
);
|
||||
Directory(
|
||||
).createSync(recursive: true);
|
||||
File(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
@@ -847,10 +851,20 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
'Products',
|
||||
'Applications',
|
||||
'Runner.app',
|
||||
'Frameworks',
|
||||
'App.framework',
|
||||
'App',
|
||||
),
|
||||
).createSync(
|
||||
recursive: true,
|
||||
);
|
||||
).createSync(recursive: true);
|
||||
File(
|
||||
p.join(
|
||||
projectRoot.path,
|
||||
'build',
|
||||
'ios',
|
||||
'shorebird',
|
||||
'App.ct.link',
|
||||
),
|
||||
).createSync(recursive: true);
|
||||
File(
|
||||
p.join(projectRoot.path, 'build', linkFileName),
|
||||
).createSync(recursive: true);
|
||||
@@ -863,6 +877,12 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
'release.xcarchive',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
supplementArtifactFile = File(
|
||||
p.join(
|
||||
Directory.systemTemp.createTempSync().path,
|
||||
'ios_supplement.zip',
|
||||
),
|
||||
)..createSync(recursive: true);
|
||||
|
||||
when(
|
||||
() => codePushClientWrapper.getReleaseArtifact(
|
||||
@@ -1269,6 +1289,75 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
);
|
||||
});
|
||||
|
||||
group('when class table link info is not present', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => artifactManager.extractZip(
|
||||
zipFile: supplementArtifactFile,
|
||||
outputDirectory: any(named: 'outputDirectory'),
|
||||
),
|
||||
).thenAnswer((invocation) async {});
|
||||
});
|
||||
|
||||
test('exits with code 70', () async {
|
||||
await expectLater(
|
||||
() => runWithOverrides(
|
||||
() => patcher.createPatchArtifacts(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
releaseArtifact: releaseArtifactFile,
|
||||
supplementArtifact: supplementArtifactFile,
|
||||
),
|
||||
),
|
||||
exitsWithCode(ExitCode.software),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => logger.err(
|
||||
'Unable to find class table link info file',
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('when class table link info is present', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => artifactManager.extractZip(
|
||||
zipFile: supplementArtifactFile,
|
||||
outputDirectory: any(named: 'outputDirectory'),
|
||||
),
|
||||
).thenAnswer((invocation) async {
|
||||
final outDir = invocation.namedArguments[#outputDirectory]
|
||||
as Directory;
|
||||
File(
|
||||
p.join(outDir.path, 'App.ct.link'),
|
||||
).createSync(recursive: true);
|
||||
});
|
||||
});
|
||||
|
||||
test('returns linked patch artifact in patch bundle', () async {
|
||||
final patchBundle = await runWithOverrides(
|
||||
() => patcher.createPatchArtifacts(
|
||||
appId: appId,
|
||||
releaseId: releaseId,
|
||||
releaseArtifact: releaseArtifactFile,
|
||||
supplementArtifact: supplementArtifactFile,
|
||||
),
|
||||
);
|
||||
|
||||
expect(patchBundle, hasLength(1));
|
||||
expect(
|
||||
patchBundle[Arch.arm64],
|
||||
isA<PatchArtifactBundle>().having(
|
||||
(b) => b.path,
|
||||
'path',
|
||||
endsWith(diffPath),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when isLinkDebugInfoSupported is true', () {
|
||||
setUp(() {
|
||||
when(
|
||||
|
||||
@@ -24,7 +24,6 @@ import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_flutter.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../fakes.dart';
|
||||
@@ -95,6 +94,17 @@ void main() {
|
||||
podfileLockHash: null,
|
||||
canSideload: true,
|
||||
);
|
||||
const supplementArtifact = ReleaseArtifact(
|
||||
id: 0,
|
||||
releaseId: 0,
|
||||
arch: arch,
|
||||
platform: releasePlatform,
|
||||
hash: '#',
|
||||
size: 422,
|
||||
url: 'https://example.com/supplement.zip',
|
||||
podfileLockHash: null,
|
||||
canSideload: false,
|
||||
);
|
||||
|
||||
late AotTools aotTools;
|
||||
late ArgResults argResults;
|
||||
@@ -225,6 +235,14 @@ void main() {
|
||||
platform: ReleasePlatform.android,
|
||||
),
|
||||
).thenAnswer((_) async => aabArtifact);
|
||||
when(
|
||||
() => codePushClientWrapper.getReleaseArtifact(
|
||||
appId: any(named: 'appId'),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
arch: 'supplement',
|
||||
platform: ReleasePlatform.android,
|
||||
),
|
||||
).thenAnswer((_) async => supplementArtifact);
|
||||
|
||||
when(
|
||||
() => logger.chooseOne<Release>(
|
||||
@@ -253,6 +271,7 @@ void main() {
|
||||
appId: any(named: 'appId'),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
releaseArtifact: any(named: 'releaseArtifact'),
|
||||
supplementArtifact: any(named: 'supplementArtifact'),
|
||||
),
|
||||
).thenAnswer((_) async => patchArtifactBundles);
|
||||
when(
|
||||
@@ -427,32 +446,84 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
group(
|
||||
'when given an existing public key and nonexistent private key',
|
||||
() {
|
||||
test('fails and logs the err', () async {
|
||||
when(
|
||||
() => argResults.wasParsed(CommonArguments.privateKeyArg.name),
|
||||
).thenReturn(false);
|
||||
when(
|
||||
() => argResults.wasParsed(CommonArguments.publicKeyArg.name),
|
||||
).thenReturn(true);
|
||||
when(
|
||||
() => argResults[CommonArguments.publicKeyArg.name],
|
||||
).thenReturn(createTempFile('public.pem').path);
|
||||
group('when given an existing public key and nonexistent private key',
|
||||
() {
|
||||
test('fails and logs the err', () async {
|
||||
when(
|
||||
() => argResults.wasParsed(CommonArguments.privateKeyArg.name),
|
||||
).thenReturn(false);
|
||||
when(
|
||||
() => argResults.wasParsed(CommonArguments.publicKeyArg.name),
|
||||
).thenReturn(true);
|
||||
when(
|
||||
() => argResults[CommonArguments.publicKeyArg.name],
|
||||
).thenReturn(createTempFile('public.pem').path);
|
||||
|
||||
await expectLater(
|
||||
runWithOverrides(() => command.createPatch(patcher)),
|
||||
exitsWithCode(ExitCode.usage),
|
||||
);
|
||||
await expectLater(
|
||||
runWithOverrides(() => command.createPatch(patcher)),
|
||||
exitsWithCode(ExitCode.usage),
|
||||
);
|
||||
verify(
|
||||
() => logger.err(
|
||||
'Both public and private keys must be provided.',
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('when a supplemental release artifact exists', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => patcher.supplementaryReleaseArtifactArch,
|
||||
).thenReturn('supplement');
|
||||
});
|
||||
|
||||
test('downloads the supplemental release artifact', () async {
|
||||
await runWithOverrides(() => command.createPatch(patcher));
|
||||
|
||||
verify(
|
||||
() => codePushClientWrapper.getReleaseArtifact(
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
arch: 'supplement',
|
||||
platform: releasePlatform,
|
||||
),
|
||||
).called(1);
|
||||
verify(
|
||||
() => patcher.createPatchArtifacts(
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
releaseArtifact: any(named: 'releaseArtifact'),
|
||||
supplementArtifact: any(named: 'supplementArtifact'),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
group('when the artifact is not found', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => codePushClientWrapper.getReleaseArtifact(
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
arch: 'supplement',
|
||||
platform: releasePlatform,
|
||||
),
|
||||
).thenThrow(CodePushNotFoundException(message: 'Not found'));
|
||||
});
|
||||
|
||||
test('gracefully continues to create patch', () async {
|
||||
await runWithOverrides(() => command.createPatch(patcher));
|
||||
verify(
|
||||
() => logger.err(
|
||||
'Both public and private keys must be provided.',
|
||||
() => patcher.createPatchArtifacts(
|
||||
appId: appId,
|
||||
releaseId: release.id,
|
||||
releaseArtifact: any(named: 'releaseArtifact'),
|
||||
supplementArtifact: any(named: 'supplementArtifact'),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -518,6 +589,10 @@ void main() {
|
||||
).thenReturn(DeploymentTrack.staging.channel);
|
||||
});
|
||||
|
||||
test('isStaging returns true', () {
|
||||
expect(command.isStaging, isTrue);
|
||||
});
|
||||
|
||||
test('logs correct summary', () async {
|
||||
final expectedSummary = [
|
||||
'''📱 App: ${lightCyan.wrap(appDisplayName)} ${lightCyan.wrap('($appId)')}''',
|
||||
|
||||
@@ -36,6 +36,20 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('supplementaryReleaseArtifactArch', () {
|
||||
test('defaults to null', () {
|
||||
expect(
|
||||
_TestPatcher(
|
||||
argParser: MockArgParser(),
|
||||
argResults: MockArgResults(),
|
||||
flavor: null,
|
||||
target: null,
|
||||
).supplementaryReleaseArtifactArch,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('assertArgsAreValid', () {
|
||||
test('has no validations by default', () {
|
||||
expect(
|
||||
@@ -261,6 +275,7 @@ class _TestPatcher extends Patcher {
|
||||
required String appId,
|
||||
required int releaseId,
|
||||
required File releaseArtifact,
|
||||
File? supplementArtifact,
|
||||
}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@@ -399,6 +399,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
appId: any(named: 'appId'),
|
||||
releaseId: any(named: 'releaseId'),
|
||||
appFrameworkPath: any(named: 'appFrameworkPath'),
|
||||
supplementPath: any(named: 'supplementPath'),
|
||||
),
|
||||
).thenAnswer((_) async {});
|
||||
});
|
||||
@@ -420,6 +421,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
|
||||
'release',
|
||||
ArtifactManager.appXcframeworkName,
|
||||
),
|
||||
supplementPath: null,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
@@ -677,6 +677,7 @@ To change the version of this release, change your app's version in your pubspec
|
||||
|
||||
late Directory xcarchiveDirectory;
|
||||
late Directory iosAppDirectory;
|
||||
late Directory supplementDirectory;
|
||||
late File podfileLockFile;
|
||||
|
||||
setUp(() {
|
||||
@@ -684,6 +685,7 @@ To change the version of this release, change your app's version in your pubspec
|
||||
|
||||
xcarchiveDirectory = Directory.systemTemp.createTempSync();
|
||||
iosAppDirectory = Directory.systemTemp.createTempSync();
|
||||
supplementDirectory = Directory.systemTemp.createTempSync();
|
||||
podfileLockFile = File(
|
||||
p.join(
|
||||
Directory.systemTemp.createTempSync().path,
|
||||
@@ -699,6 +701,9 @@ To change the version of this release, change your app's version in your pubspec
|
||||
xcarchiveDirectory: any(named: 'xcarchiveDirectory'),
|
||||
),
|
||||
).thenReturn(iosAppDirectory);
|
||||
when(
|
||||
() => artifactManager.getIosReleaseSupplementDirectory(),
|
||||
).thenReturn(supplementDirectory);
|
||||
when(
|
||||
() => codePushClientWrapper.createIosReleaseArtifacts(
|
||||
appId: any(named: 'appId'),
|
||||
@@ -707,6 +712,7 @@ To change the version of this release, change your app's version in your pubspec
|
||||
runnerPath: any(named: 'runnerPath'),
|
||||
isCodesigned: any(named: 'isCodesigned'),
|
||||
podfileLockHash: any(named: 'podfileLockHash'),
|
||||
supplementPath: any(named: 'supplementPath'),
|
||||
),
|
||||
).thenAnswer((_) async => {});
|
||||
when(() => shorebirdEnv.podfileLockFile).thenReturn(podfileLockFile);
|
||||
@@ -728,7 +734,8 @@ To change the version of this release, change your app's version in your pubspec
|
||||
runnerPath: iosAppDirectory.path,
|
||||
isCodesigned: codesign,
|
||||
podfileLockHash:
|
||||
sha256.convert(utf8.encode(podfileLockContent)).toString(),
|
||||
'${sha256.convert(utf8.encode(podfileLockContent))}',
|
||||
supplementPath: supplementDirectory.path,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user