feat(shorebird_cli): record signed patches in release and patch metadata (#3394)

This commit is contained in:
Bryan Oltman
2025-11-20 16:46:43 -05:00
committed by GitHub
parent 2ec5af7cf6
commit 7d9964ff2a
19 changed files with 187 additions and 0 deletions
@@ -447,6 +447,7 @@ Building patch with Flutter $flutterVersionString
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: diffStatus.hasNativeChanges,
inferredReleaseVersion: inferredReleaseVersion,
isSigned: results.wasParsed(CommonArguments.privateKeyArg.name),
environment: BuildEnvironmentMetadata(
flutterRevision: shorebirdEnv.flutterRevision,
operatingSystem: platform.operatingSystem,
@@ -564,6 +564,7 @@ ${summary.join('\n')}
final baseMetadata = UpdateReleaseMetadata(
releasePlatform: releaser.releaseType.releasePlatform,
flutterVersionOverride: flutterVersionArg,
includesPublicKey: results.wasParsed(CommonArguments.publicKeyArg.name),
environment: BuildEnvironmentMetadata(
flutterRevision: shorebirdEnv.flutterRevision,
operatingSystem: platform.operatingSystem,
@@ -26,6 +26,7 @@ class CreatePatchMetadata extends Equatable {
required this.hasNativeChanges,
required this.inferredReleaseVersion,
required this.environment,
required this.isSigned,
this.linkPercentage,
this.linkMetadata,
});
@@ -40,6 +41,7 @@ class CreatePatchMetadata extends Equatable {
bool usedIgnoreNativeChangesFlag = false,
bool hasNativeChanges = false,
bool inferredReleaseVersion = false,
bool isSigned = false,
double? linkPercentage,
Json? linkMetadata,
BuildEnvironmentMetadata? environment,
@@ -49,6 +51,7 @@ class CreatePatchMetadata extends Equatable {
hasAssetChanges: hasAssetChanges,
usedIgnoreNativeChangesFlag: usedIgnoreNativeChangesFlag,
hasNativeChanges: hasNativeChanges,
isSigned: isSigned,
inferredReleaseVersion: inferredReleaseVersion,
linkPercentage: linkPercentage,
linkMetadata: linkMetadata,
@@ -72,6 +75,7 @@ class CreatePatchMetadata extends Equatable {
bool? usedIgnoreNativeChangesFlag,
bool? hasNativeChanges,
bool? inferredReleaseVersion,
bool? isSigned,
double? linkPercentage,
Json? linkMetadata,
BuildEnvironmentMetadata? environment,
@@ -85,6 +89,7 @@ class CreatePatchMetadata extends Equatable {
hasNativeChanges: hasNativeChanges ?? this.hasNativeChanges,
inferredReleaseVersion:
inferredReleaseVersion ?? this.inferredReleaseVersion,
isSigned: isSigned ?? this.isSigned,
linkPercentage: linkPercentage ?? this.linkPercentage,
linkMetadata: linkMetadata ?? this.linkMetadata,
environment: environment ?? this.environment,
@@ -132,6 +137,13 @@ class CreatePatchMetadata extends Equatable {
/// Metadata from the linker, if available.
final Json? linkMetadata;
/// Whether the patch was signed.
///
/// Reason: this helps us understand how often users are signing their
/// patches, and helps us provide better support for users who encounter
/// issues.
final bool isSigned;
/// Properties about the environment in which the patch was created.
///
/// Reason: see [BuildEnvironmentMetadata].
@@ -147,6 +159,7 @@ class CreatePatchMetadata extends Equatable {
linkPercentage,
linkMetadata,
inferredReleaseVersion,
isSigned,
environment,
];
}
@@ -37,6 +37,7 @@ CreatePatchMetadata _$CreatePatchMetadataFromJson(
'environment',
(v) => BuildEnvironmentMetadata.fromJson(v as Map<String, dynamic>),
),
isSigned: $checkedConvert('is_signed', (v) => v as bool),
linkPercentage: $checkedConvert(
'link_percentage',
(v) => (v as num?)?.toDouble(),
@@ -55,6 +56,7 @@ CreatePatchMetadata _$CreatePatchMetadataFromJson(
'usedIgnoreNativeChangesFlag': 'used_ignore_native_changes_flag',
'hasNativeChanges': 'has_native_changes',
'inferredReleaseVersion': 'inferred_release_version',
'isSigned': 'is_signed',
'linkPercentage': 'link_percentage',
'linkMetadata': 'link_metadata',
},
@@ -71,6 +73,7 @@ Map<String, dynamic> _$CreatePatchMetadataToJson(
'inferred_release_version': instance.inferredReleaseVersion,
'link_percentage': instance.linkPercentage,
'link_metadata': instance.linkMetadata,
'is_signed': instance.isSigned,
'environment': instance.environment.toJson(),
};
@@ -22,6 +22,7 @@ class UpdateReleaseMetadata extends Equatable {
required this.releasePlatform,
required this.flutterVersionOverride,
required this.environment,
required this.includesPublicKey,
this.generatedApks,
});
@@ -32,12 +33,14 @@ class UpdateReleaseMetadata extends Equatable {
ReleasePlatform releasePlatform = ReleasePlatform.android,
String? flutterVersionOverride = '1.2.3',
bool? generatedApks = false,
bool includesPublicKey = false,
BuildEnvironmentMetadata? environment,
}) => UpdateReleaseMetadata(
releasePlatform: releasePlatform,
flutterVersionOverride: flutterVersionOverride,
generatedApks: generatedApks,
environment: environment ?? BuildEnvironmentMetadata.forTest(),
includesPublicKey: includesPublicKey,
);
// coverage:ignore-end
@@ -55,12 +58,14 @@ class UpdateReleaseMetadata extends Equatable {
String? flutterVersionOverride,
bool? generatedApks,
BuildEnvironmentMetadata? environment,
bool? includesPublicKey,
}) => UpdateReleaseMetadata(
releasePlatform: releasePlatform ?? this.releasePlatform,
flutterVersionOverride:
flutterVersionOverride ?? this.flutterVersionOverride,
generatedApks: generatedApks ?? this.generatedApks,
environment: environment ?? this.environment,
includesPublicKey: includesPublicKey ?? this.includesPublicKey,
);
/// The platform for which the patch was created.
@@ -79,6 +84,13 @@ class UpdateReleaseMetadata extends Equatable {
/// which may affect the build process.
final bool? generatedApks;
/// Whether the user included a public key for the release.
///
/// Reason: this helps us understand how often users are signing their
/// patches, and helps us provide better support for users who encounter
/// issues.
final bool? includesPublicKey;
/// Properties about the environment in which the update to the release was
/// performed.
///
@@ -90,6 +102,7 @@ class UpdateReleaseMetadata extends Equatable {
releasePlatform,
flutterVersionOverride,
generatedApks,
includesPublicKey,
environment,
];
}
@@ -27,6 +27,10 @@ UpdateReleaseMetadata _$UpdateReleaseMetadataFromJson(
'environment',
(v) => BuildEnvironmentMetadata.fromJson(v as Map<String, dynamic>),
),
includesPublicKey: $checkedConvert(
'includes_public_key',
(v) => v as bool?,
),
generatedApks: $checkedConvert('generated_apks', (v) => v as bool?),
);
return val;
@@ -34,6 +38,7 @@ UpdateReleaseMetadata _$UpdateReleaseMetadataFromJson(
fieldKeyMap: const {
'releasePlatform': 'release_platform',
'flutterVersionOverride': 'flutter_version_override',
'includesPublicKey': 'includes_public_key',
'generatedApks': 'generated_apks',
},
);
@@ -44,6 +49,7 @@ Map<String, dynamic> _$UpdateReleaseMetadataToJson(
'release_platform': _$ReleasePlatformEnumMap[instance.releasePlatform]!,
'flutter_version_override': instance.flutterVersionOverride,
'generated_apks': instance.generatedApks,
'includes_public_key': instance.includesPublicKey,
'environment': instance.environment.toJson(),
};
@@ -628,6 +628,7 @@ void main() {
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -730,6 +730,7 @@ Looked in:
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1017,6 +1017,7 @@ void main() {
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: true,
inferredReleaseVersion: false,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1039,6 +1040,7 @@ void main() {
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: true,
inferredReleaseVersion: false,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1072,6 +1074,7 @@ void main() {
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1096,6 +1099,7 @@ void main() {
inferredReleaseVersion: false,
linkPercentage: linkPercentage,
linkMetadata: linkMetadata,
isSigned: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1534,6 +1534,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: true,
inferredReleaseVersion: false,
isSigned: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1556,6 +1557,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: true,
inferredReleaseVersion: false,
isSigned: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1589,6 +1591,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1613,6 +1616,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
inferredReleaseVersion: false,
linkPercentage: linkPercentage,
linkMetadata: linkMetadata,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1152,6 +1152,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1172,6 +1173,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
usedIgnoreNativeChangesFlag: allowNativeDiffs,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -1486,6 +1486,60 @@ Please re-run the release command for this version or create a new release.'''),
).called(1);
});
});
group('reported patch metadata', () {
group('when signing keys are provided', () {
setUp(() {
when(
() => argResults.wasParsed(CommonArguments.publicKeyArg.name),
).thenReturn(true);
when(
() => argResults.wasParsed(CommonArguments.privateKeyArg.name),
).thenReturn(true);
});
test('sets isSigned to true in PatchMetadata', () async {
await runWithOverrides(command.run);
verify(
() => patcher.updatedCreatePatchMetadata(
any(
that: isA<CreatePatchMetadata>().having(
(m) => m.isSigned,
'isSigned',
isTrue,
),
),
),
);
});
});
group('when no signing keys are provided', () {
setUp(() {
when(
() => argResults.wasParsed(CommonArguments.publicKeyArg.name),
).thenReturn(false);
when(
() => argResults.wasParsed(CommonArguments.privateKeyArg.name),
).thenReturn(false);
});
test('sets isSigned to false in PatchMetadata', () async {
await runWithOverrides(command.run);
verify(
() => patcher.updatedCreatePatchMetadata(
any(
that: isA<CreatePatchMetadata>().having(
(m) => m.isSigned,
'isSigned',
isFalse,
),
),
),
);
});
});
});
});
group('sortByUpdatedAt', () {
@@ -592,6 +592,7 @@ To change the version of this release, change your app's version in your pubspec
const metadata = UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: null,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -617,6 +618,7 @@ To change the version of this release, change your app's version in your pubspec
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: null,
generatedApks: true,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -646,6 +648,7 @@ To change the version of this release, change your app's version in your pubspec
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: null,
generatedApks: false,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -448,6 +448,7 @@ void main() {
const metadata = UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.ios,
flutterVersionOverride: null,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -471,6 +472,7 @@ void main() {
const UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.ios,
flutterVersionOverride: null,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -687,6 +687,7 @@ To change the version of this release, change your app's version in your pubspec
const metadata = UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.ios,
flutterVersionOverride: flutterVersionOverride,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -708,6 +709,7 @@ To change the version of this release, change your app's version in your pubspec
const UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.ios,
flutterVersionOverride: flutterVersionOverride,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -614,6 +614,7 @@ To change the version of this release, change your app's version in your pubspec
const metadata = UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.macos,
flutterVersionOverride: flutterVersionOverride,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -635,6 +636,7 @@ To change the version of this release, change your app's version in your pubspec
const UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.macos,
flutterVersionOverride: flutterVersionOverride,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: flutterRevision,
operatingSystem: operatingSystem,
@@ -713,5 +713,63 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}'''),
).called(1);
});
});
group('finalizeRelease', () {
group('when a public key is provided', () {
setUp(() {
when(
() => argResults.wasParsed(CommonArguments.publicKeyArg.name),
).thenReturn(true);
});
test('completes successfully', () async {
await runWithOverrides(
() => command.finalizeRelease(
release: release,
releaser: releaser,
),
);
verify(
() => releaser.updatedReleaseMetadata(
any(
that: isA<UpdateReleaseMetadata>().having(
(metadata) => metadata.includesPublicKey,
'includesPublicKey',
isTrue,
),
),
),
).called(1);
});
});
group('when no public key is provided', () {
setUp(() {
when(
() => argResults.wasParsed(CommonArguments.publicKeyArg.name),
).thenReturn(false);
});
test('completes successfully', () async {
await runWithOverrides(
() => command.finalizeRelease(
release: release,
releaser: releaser,
),
);
verify(
() => releaser.updatedReleaseMetadata(
any(
that: isA<UpdateReleaseMetadata>().having(
(metadata) => metadata.includesPublicKey,
'includesPublicKey',
isFalse,
),
),
),
).called(1);
});
});
});
});
}
@@ -13,6 +13,7 @@ void main() {
usedIgnoreNativeChangesFlag: false,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: false,
linkPercentage: 99.9,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -39,6 +40,7 @@ void main() {
usedIgnoreNativeChangesFlag: false,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: false,
linkPercentage: 99.9,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -62,6 +64,7 @@ void main() {
usedIgnoreNativeChangesFlag: false,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: true,
linkPercentage: 99.9,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -102,6 +105,7 @@ void main() {
usedIgnoreNativeChangesFlag: true,
hasNativeChanges: true,
inferredReleaseVersion: false,
isSigned: true,
linkPercentage: 99.8,
environment: BuildEnvironmentMetadata(
flutterRevision: 'asdf',
@@ -128,6 +132,7 @@ void main() {
hasNativeChanges: false,
inferredReleaseVersion: false,
linkPercentage: 99.9,
isSigned: true,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',
@@ -145,6 +150,7 @@ void main() {
usedIgnoreNativeChangesFlag: false,
hasNativeChanges: false,
inferredReleaseVersion: false,
isSigned: true,
linkPercentage: 99.9,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -166,6 +172,7 @@ void main() {
hasAssetChanges: false,
usedIgnoreNativeChangesFlag: false,
hasNativeChanges: false,
isSigned: true,
inferredReleaseVersion: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -183,6 +190,7 @@ void main() {
hasAssetChanges: false,
usedIgnoreNativeChangesFlag: false,
hasNativeChanges: false,
isSigned: false,
inferredReleaseVersion: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -10,6 +10,7 @@ void main() {
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.3',
generatedApks: false,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',
@@ -32,6 +33,7 @@ void main() {
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.3',
generatedApks: false,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',
@@ -50,6 +52,7 @@ void main() {
const metadata = UpdateReleaseMetadata(
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.3',
includesPublicKey: false,
generatedApks: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
@@ -66,6 +69,7 @@ void main() {
releasePlatform: ReleasePlatform.ios,
flutterVersionOverride: '1.2.4',
generatedApks: true,
includesPublicKey: true,
environment: const BuildEnvironmentMetadata(
flutterRevision: 'asdf',
operatingSystem: 'windows',
@@ -84,6 +88,7 @@ void main() {
releasePlatform: ReleasePlatform.ios,
flutterVersionOverride: '1.2.4',
generatedApks: true,
includesPublicKey: true,
environment: BuildEnvironmentMetadata(
flutterRevision: 'asdf',
operatingSystem: 'windows',
@@ -105,6 +110,7 @@ void main() {
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.3',
generatedApks: false,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',
@@ -119,6 +125,7 @@ void main() {
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.3',
generatedApks: false,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',
@@ -137,6 +144,7 @@ void main() {
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.3',
generatedApks: false,
includesPublicKey: true,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',
@@ -151,6 +159,7 @@ void main() {
releasePlatform: ReleasePlatform.android,
flutterVersionOverride: '1.2.4',
generatedApks: false,
includesPublicKey: false,
environment: BuildEnvironmentMetadata(
flutterRevision: '853d13d954df3b6e9c2f07b72062f33c52a9a64b',
operatingSystem: 'macos',