From 825f3596d9951aa93f7a72bc5d4e29d276ec8312 Mon Sep 17 00:00:00 2001 From: Eric Seidel Date: Fri, 16 Jan 2026 16:57:03 -0700 Subject: [PATCH] feat: support patch_verification config in shorebird.yaml (#3463) --- bin/internal/flutter.version | 2 +- .../lib/src/config/shorebird_yaml.dart | 15 +++++ .../lib/src/config/shorebird_yaml.g.dart | 34 +++++++++--- .../test/src/config/shorebird_yaml_test.dart | 55 +++++++++++++++++++ 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/bin/internal/flutter.version b/bin/internal/flutter.version index db452681..2ca182b1 100644 --- a/bin/internal/flutter.version +++ b/bin/internal/flutter.version @@ -1 +1 @@ -55c197eb61ef2219a709002c39ffa445b05600f8 \ No newline at end of file +8f934ceac62ce289fda9492b0dfc846ecb214fc6 \ No newline at end of file diff --git a/packages/shorebird_cli/lib/src/config/shorebird_yaml.dart b/packages/shorebird_cli/lib/src/config/shorebird_yaml.dart index 29aecd5f..f7906e85 100644 --- a/packages/shorebird_cli/lib/src/config/shorebird_yaml.dart +++ b/packages/shorebird_cli/lib/src/config/shorebird_yaml.dart @@ -2,6 +2,17 @@ import 'package:json_annotation/json_annotation.dart'; part 'shorebird_yaml.g.dart'; +/// The patch verification mode for the app. +@JsonEnum(fieldRename: FieldRename.snake) +enum PatchVerification { + /// Verify the patch signature and hash before installing and loading. + strict, + + /// Verify the patch signature and hash before installing, but not when + /// loading from cache. + installOnly, +} + /// {@template shorebird_yaml} /// A Shorebird configuration file which contains metadata about the app. /// {@endtemplate} @@ -13,6 +24,7 @@ class ShorebirdYaml { this.flavors, this.baseUrl, this.autoUpdate, + this.patchVerification, }); /// Creates a [ShorebirdYaml] from a JSON map. @@ -46,6 +58,9 @@ class ShorebirdYaml { /// Whether or not to automatically update the app. final bool? autoUpdate; + + /// The patch verification mode for the app. + final PatchVerification? patchVerification; } /// Extension on [ShorebirdYaml] to get the app id for a specific flavor. diff --git a/packages/shorebird_cli/lib/src/config/shorebird_yaml.g.dart b/packages/shorebird_cli/lib/src/config/shorebird_yaml.g.dart index bbf1c90f..32a49a5d 100644 --- a/packages/shorebird_cli/lib/src/config/shorebird_yaml.g.dart +++ b/packages/shorebird_cli/lib/src/config/shorebird_yaml.g.dart @@ -14,7 +14,13 @@ ShorebirdYaml _$ShorebirdYamlFromJson(Map json) => $checkedCreate( ($checkedConvert) { $checkKeys( json, - allowedKeys: const ['app_id', 'flavors', 'base_url', 'auto_update'], + allowedKeys: const [ + 'app_id', + 'flavors', + 'base_url', + 'auto_update', + 'patch_verification', + ], ); final val = ShorebirdYaml( appId: $checkedConvert('app_id', (v) => v as String), @@ -24,6 +30,10 @@ ShorebirdYaml _$ShorebirdYamlFromJson(Map json) => $checkedCreate( ), baseUrl: $checkedConvert('base_url', (v) => v as String?), autoUpdate: $checkedConvert('auto_update', (v) => v as bool?), + patchVerification: $checkedConvert( + 'patch_verification', + (v) => $enumDecodeNullable(_$PatchVerificationEnumMap, v), + ), ); return val; }, @@ -31,13 +41,21 @@ ShorebirdYaml _$ShorebirdYamlFromJson(Map json) => $checkedCreate( 'appId': 'app_id', 'baseUrl': 'base_url', 'autoUpdate': 'auto_update', + 'patchVerification': 'patch_verification', }, ); -Map _$ShorebirdYamlToJson(ShorebirdYaml instance) => - { - 'app_id': instance.appId, - 'flavors': instance.flavors, - 'base_url': instance.baseUrl, - 'auto_update': instance.autoUpdate, - }; +Map _$ShorebirdYamlToJson( + ShorebirdYaml instance, +) => { + 'app_id': instance.appId, + 'flavors': instance.flavors, + 'base_url': instance.baseUrl, + 'auto_update': instance.autoUpdate, + 'patch_verification': _$PatchVerificationEnumMap[instance.patchVerification], +}; + +const _$PatchVerificationEnumMap = { + PatchVerification.strict: 'strict', + PatchVerification.installOnly: 'install_only', +}; diff --git a/packages/shorebird_cli/test/src/config/shorebird_yaml_test.dart b/packages/shorebird_cli/test/src/config/shorebird_yaml_test.dart index 569b073b..736be958 100644 --- a/packages/shorebird_cli/test/src/config/shorebird_yaml_test.dart +++ b/packages/shorebird_cli/test/src/config/shorebird_yaml_test.dart @@ -67,6 +67,61 @@ auto_update: true expect(shorebirdYaml.autoUpdate, isTrue); }); + test('can be deserialized without patch_verification', () { + const yaml = ''' +app_id: test_app_id +'''; + final shorebirdYaml = checkedYamlDecode( + yaml, + (m) => ShorebirdYaml.fromJson(m!), + ); + expect(shorebirdYaml.appId, 'test_app_id'); + expect(shorebirdYaml.patchVerification, isNull); + }); + + test('can be deserialized with patch_verification: strict', () { + const yaml = ''' +app_id: test_app_id +patch_verification: strict +'''; + final shorebirdYaml = checkedYamlDecode( + yaml, + (m) => ShorebirdYaml.fromJson(m!), + ); + expect(shorebirdYaml.appId, 'test_app_id'); + expect(shorebirdYaml.patchVerification, PatchVerification.strict); + }); + + test('can be deserialized with patch_verification: install_only', () { + const yaml = ''' +app_id: test_app_id +patch_verification: install_only +'''; + final shorebirdYaml = checkedYamlDecode( + yaml, + (m) => ShorebirdYaml.fromJson(m!), + ); + expect(shorebirdYaml.appId, 'test_app_id'); + expect(shorebirdYaml.patchVerification, PatchVerification.installOnly); + }); + + test('throws when patch_verification has invalid value', () { + const yaml = ''' +app_id: test_app_id +patch_verification: invalid_value +'''; + expect( + () => checkedYamlDecode(yaml, (m) => ShorebirdYaml.fromJson(m!)), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('patch_verification'), + ), + ), + ); + }); + group('AppIdExtension', () { test('getAppId returns base app id when no flavor is provided', () { const shorebirdYaml = ShorebirdYaml(appId: 'test_app_id');