feat: adding signing hash for ios patches (#2144)
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user