feat: adding signing hash for ios patches (#2144)

This commit is contained in:
Erick
2024-05-24 14:04:40 -03:00
committed by GitHub
parent e58e6abe60
commit bdbb623fa9
11 changed files with 98 additions and 17 deletions
@@ -8,6 +8,7 @@ import 'package:shorebird_cli/src/artifact_manager.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/code_signer.dart';
import 'package:shorebird_cli/src/commands/patch/patcher.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/extensions/arg_results.dart';
import 'package:shorebird_cli/src/extensions/file.dart';
@@ -36,7 +37,7 @@ class AndroidPatcher extends Patcher {
@override
Future<void> assertArgsAreValid() async {
argResults.file('private-key-path')?.assertExists();
argResults.file(CommonArguments.privateKeyArgName)?.assertExists();
}
@override
@@ -157,7 +158,7 @@ Looked in:
final patchArtifact = File(patchArtifactPath);
final hash = sha256.convert(await patchArtifact.readAsBytes()).toString();
final privateKeyFile = argResults.file('private-key-path');
final privateKeyFile = argResults.file(CommonArguments.privateKeyArgName);
final hashSignature = privateKeyFile != null
? codeSigner.sign(
message: hash,
@@ -9,9 +9,12 @@ import 'package:shorebird_cli/src/archive_analysis/archive_differ.dart';
import 'package:shorebird_cli/src/artifact_builder.dart';
import 'package:shorebird_cli/src/artifact_manager.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/code_signer.dart';
import 'package:shorebird_cli/src/commands/patch/patcher.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/executables/executables.dart';
import 'package:shorebird_cli/src/extensions/arg_results.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/patch_diff_checker.dart';
import 'package:shorebird_cli/src/platform.dart';
@@ -210,12 +213,22 @@ class IosPatcher extends Patcher {
}
final patchFileSize = patchFile.statSync().size;
final privateKeyFile = argResults.file(CommonArguments.privateKeyArgName);
final hash = sha256.convert(patchBuildFile.readAsBytesSync()).toString();
final hashSignature = privateKeyFile != null
? codeSigner.sign(
message: hash,
privateKeyPemFile: privateKeyFile,
)
: null;
return {
Arch.arm64: PatchArtifactBundle(
arch: 'aarch64',
path: patchFile.path,
hash: sha256.convert(patchBuildFile.readAsBytesSync()).toString(),
hash: hash,
size: patchFileSize,
hashSignature: hashSignature,
),
};
}
@@ -8,6 +8,7 @@ import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/commands/patch/patch.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/deployment_track.dart';
import 'package:shorebird_cli/src/extensions/arg_results.dart';
@@ -94,7 +95,7 @@ of the iOS app that is using this module.''',
help: 'Validate but do not upload the patch.',
)
..addOption(
'private-key-path',
CommonArguments.privateKeyArgName,
hide: true,
help: '''
The path for a private key file that will be used to sign the patch artifact.
@@ -7,6 +7,7 @@ import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/commands/release/release.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/extensions/arg_results.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -116,7 +117,7 @@ of the iOS app that is using this module.''',
allowed: Arch.values.map((arch) => arch.targetPlatformCliArg),
)
..addOption(
'public-key-path',
CommonArguments.publicKeyArgName,
hide: true,
help: '''
The path for a public key file that will be used to validate patch signatures.
@@ -0,0 +1,6 @@
/// A class that houses the name of arguments that are shared between different
/// commands and layers.
class CommonArguments {
static const String publicKeyArgName = 'public-key-path';
static const String privateKeyArgName = 'private-key-path';
}
@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:collection/collection.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/extensions/file.dart';
extension OptionFinder on ArgResults {
@@ -44,18 +45,16 @@ extension OptionFinder on ArgResults {
}
}
const _publicKeyArgName = 'public-key-path';
extension CodeSign on ArgResults {
/// Asserts that either there is no public key argument
/// or that the path received exists.
void assertAbsentOrValidPublicKey() {
file(_publicKeyArgName)?.assertExists();
file(CommonArguments.publicKeyArgName)?.assertExists();
}
/// Read the public key file and encode it to base64 if any.
String? get encodedPublicKey {
final publicKeyFile = file(_publicKeyArgName);
final publicKeyFile = file(CommonArguments.publicKeyArgName);
return publicKeyFile != null
? base64Encode(publicKeyFile.readAsBytesSync())
@@ -11,6 +11,7 @@ import 'package:shorebird_cli/src/artifact_manager.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/code_signer.dart';
import 'package:shorebird_cli/src/commands/patch/patch.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/engine_config.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -466,7 +467,7 @@ Looked in:
),
)..createSync();
when(() => argResults['private-key-path'])
when(() => argResults[CommonArguments.privateKeyArgName])
.thenReturn(privateKey.path);
when(
@@ -8,7 +8,9 @@ import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
import 'package:shorebird_cli/src/artifact_builder.dart';
import 'package:shorebird_cli/src/artifact_manager.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/code_signer.dart';
import 'package:shorebird_cli/src/commands/patch/patch.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/engine_config.dart';
import 'package:shorebird_cli/src/executables/aot_tools.dart';
@@ -43,6 +45,7 @@ void main() {
late ArtifactBuilder artifactBuilder;
late ArtifactManager artifactManager;
late CodePushClientWrapper codePushClientWrapper;
late CodeSigner codeSigner;
late Doctor doctor;
late EngineConfig engineConfig;
late Directory flutterDirectory;
@@ -69,6 +72,7 @@ void main() {
artifactBuilderRef.overrideWith(() => artifactBuilder),
artifactManagerRef.overrideWith(() => artifactManager),
codePushClientWrapperRef.overrideWith(() => codePushClientWrapper),
codeSignerRef.overrideWith(() => codeSigner),
doctorRef.overrideWith(() => doctor),
engineConfigRef.overrideWith(() => engineConfig),
iosRef.overrideWith(() => ios),
@@ -102,6 +106,7 @@ void main() {
artifactBuilder = MockArtifactBuilder();
artifactManager = MockArtifactManager();
codePushClientWrapper = MockCodePushClientWrapper();
codeSigner = MockCodeSigner();
doctor = MockDoctor();
engineConfig = MockEngineConfig();
ios = MockIos();
@@ -935,6 +940,56 @@ void main() {
).called(1);
});
});
group('when a private key is provided', () {
setUp(() {
final privateKey = File(
p.join(
Directory.systemTemp.createTempSync().path,
'test-private.pem',
),
)..createSync();
when(() => argResults[CommonArguments.privateKeyArgName])
.thenReturn(privateKey.path);
when(
() => codeSigner.sign(
message: any(named: 'message'),
privateKeyPemFile: any(named: 'privateKeyPemFile'),
),
).thenAnswer((invocation) {
final message =
invocation.namedArguments[#message] as String;
return '$message-signature';
});
});
test(
'''returns patch artifact bundles with proper hash signatures''',
() async {
final result = await runWithOverrides(
() => patcher.createPatchArtifacts(
appId: appId,
releaseId: releaseId,
releaseArtifact: releaseArtifactFile,
),
);
// Hash the patch artifacts and append '-signature' to get the
// expected signatures, per the mock of [codeSigner.sign]
// above.
const expectedSignature =
'''e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855-signature''';
expect(
result.values.first.hashSignature,
equals(
expectedSignature,
),
);
});
});
});
});
@@ -10,6 +10,7 @@ import 'package:shorebird_cli/src/artifact_builder.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/code_signer.dart';
import 'package:shorebird_cli/src/commands/release/android_releaser.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/engine_config.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -228,7 +229,7 @@ void main() {
'public-key.der',
),
)..writeAsStringSync('public key');
when(() => argResults['public-key-path'])
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn(publicKeyFile.path);
});
@@ -243,7 +244,7 @@ void main() {
group('when a public key is provided but it does not exists', () {
setUp(() {
when(() => argResults['artifact']).thenReturn('apk');
when(() => argResults['public-key-path'])
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn('non-existing-key.der');
});
@@ -435,7 +436,7 @@ void main() {
'patch-signing-public-key.der',
),
)..writeAsStringSync('public key');
when(() => argResults['public-key-path'])
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn(patchSigningPublicKeyFile.path);
when(
@@ -10,6 +10,7 @@ import 'package:shorebird_cli/src/artifact_builder.dart';
import 'package:shorebird_cli/src/artifact_manager.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/commands/release/ios_releaser.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/executables/xcodebuild.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -254,7 +255,7 @@ void main() {
'public-key.der',
),
)..writeAsStringSync('public key');
when(() => argResults['public-key-path'])
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn(publicKeyFile.path);
});
@@ -268,7 +269,7 @@ void main() {
group('when the provided public key is a nonexistent file', () {
setUp(() {
when(() => argResults['public-key-path'])
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn('non-existing-key.der');
});
@@ -331,7 +332,7 @@ void main() {
'patch-signing-public-key.der',
),
)..writeAsStringSync('public key');
when(() => argResults['public-key-path'])
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn(patchSigningPublicKeyFile.path);
when(
@@ -6,6 +6,7 @@ import 'package:scoped_deps/scoped_deps.dart';
import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/commands/release/release.dart';
import 'package:shorebird_cli/src/common_arguments.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/release_type.dart';
@@ -500,7 +501,8 @@ $exception''',
keyName,
),
)..writeAsStringSync('KEY');
when(() => argResults['public-key-path']).thenReturn(file.path);
when(() => argResults[CommonArguments.publicKeyArgName])
.thenReturn(file.path);
});
test('completes successfully', () async {