diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 806c81a..419bcf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,29 @@ jobs: fi echo "OK: both packages are at $CORE_VERSION" + # Gate on a perfect pub.dev score for both packages. pana is the analyzer + # behind pub.dev's "pub points"; --exit-code-threshold 0 fails the job when + # any points are lost (max - granted > 0), so every release stays at + # 160/160. Runs on a clean checkout so pana resolves the package itself. + pana: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: + - . + - flutter_meta_wearables_dat_mock_device + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: stable + - name: Activate pana + run: dart pub global activate pana + - name: pana (require full pub points) + working-directory: ${{ matrix.package }} + run: dart pub global run pana --no-warning --exit-code-threshold 0 . + # Build the example app on both iOS resolvers. Example app is committed in # its CocoaPods baseline state; on the SPM job Flutter migrates the # pbxproj on the fly. Catches regressions on either path. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 50ff564..13ba102 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -50,6 +50,17 @@ jobs: dart analyze --fatal-infos lib/ if [ -d test ]; then flutter test; else echo "No test directory, skipping"; fi + # Hard quality gate: refuse to publish unless BOTH packages score a + # perfect 160/160 on pana (the analyzer behind pub.dev's pub points). + # --exit-code-threshold 0 fails the job if any points are lost. + - name: Activate pana + run: dart pub global activate pana + - name: pana — core (require full pub points) + run: dart pub global run pana --no-warning --exit-code-threshold 0 . + - name: pana — mock add-on (require full pub points) + working-directory: flutter_meta_wearables_dat_mock_device + run: dart pub global run pana --no-warning --exit-code-threshold 0 . + # Publish core first; if the mock add-on fails afterwards, re-tag # patch-level after fixing — the core publish is not retried, so a # half-publish is recoverable by bumping both packages to the next diff --git a/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device.dart b/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device.dart index 331364c..3d2b896 100644 --- a/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device.dart +++ b/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device.dart @@ -178,10 +178,7 @@ class MetaWearablesDatMockDevice { /// /// Mutually exclusive with [setCameraFeed] — calling either clears /// the source configured by the other. - static Future setCameraFacing( - String deviceUUID, - CameraFacing facing, - ) { + static Future setCameraFacing(String deviceUUID, CameraFacing facing) { return MetaWearablesDatMockDevicePlatform.instance.setCameraFacing( deviceUUID, facing, diff --git a/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_method_channel.dart b/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_method_channel.dart index 01cc82e..4aa00e5 100644 --- a/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_method_channel.dart +++ b/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_method_channel.dart @@ -116,10 +116,7 @@ class MethodChannelMetaWearablesDatMockDevice } @override - Future setCameraFacing( - String deviceUUID, - CameraFacing facing, - ) async { + Future setCameraFacing(String deviceUUID, CameraFacing facing) async { final ok = await methodChannel.invokeMethod('setCameraFacing', { 'deviceUUID': deviceUUID, 'cameraFacing': facing.value, diff --git a/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_platform_interface.dart b/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_platform_interface.dart index 4ea22ce..ab8bf03 100644 --- a/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_platform_interface.dart +++ b/flutter_meta_wearables_dat_mock_device/lib/flutter_meta_wearables_dat_mock_device_platform_interface.dart @@ -47,10 +47,7 @@ abstract class MetaWearablesDatMockDevicePlatform extends PlatformInterface { throw UnimplementedError('unpairGlasses() has not been implemented.'); } - Future setPermission( - Permission permission, - PermissionStatus status, - ) { + Future setPermission(Permission permission, PermissionStatus status) { throw UnimplementedError('setPermission() has not been implemented.'); } diff --git a/flutter_meta_wearables_dat_mock_device/pubspec.yaml b/flutter_meta_wearables_dat_mock_device/pubspec.yaml index f5a9ae5..12fa3fe 100644 --- a/flutter_meta_wearables_dat_mock_device/pubspec.yaml +++ b/flutter_meta_wearables_dat_mock_device/pubspec.yaml @@ -1,5 +1,5 @@ name: flutter_meta_wearables_dat_mock_device -description: "Optional MockDeviceKit add-on for flutter_meta_wearables_dat — simulates a Ray-Ban Meta device using the phone's camera. Pull this in only for development and testing; production apps should omit it to avoid linking AVFoundation/Camera symbols." +description: "Optional MockDeviceKit add-on for flutter_meta_wearables_dat — simulate Meta glasses using the phone's camera for development and testing. Omit in production." version: 0.7.0 repository: https://github.com/rodcone/flutter_meta_wearables_dat diff --git a/flutter_meta_wearables_dat_mock_device/test/flutter_meta_wearables_dat_mock_device_method_channel_test.dart b/flutter_meta_wearables_dat_mock_device/test/flutter_meta_wearables_dat_mock_device_method_channel_test.dart index 20b38e3..f6f13cb 100644 --- a/flutter_meta_wearables_dat_mock_device/test/flutter_meta_wearables_dat_mock_device_method_channel_test.dart +++ b/flutter_meta_wearables_dat_mock_device/test/flutter_meta_wearables_dat_mock_device_method_channel_test.dart @@ -52,17 +52,20 @@ void main() { expect(calls.single.arguments, {'deviceUUID': 'uuid-123'}); }); - test('facade delegates pairGlasses(model) to the platform instance', () async { - final fake = _FakeMockPlatform(); - MetaWearablesDatMockDevicePlatform.instance = fake; + test( + 'facade delegates pairGlasses(model) to the platform instance', + () async { + final fake = _FakeMockPlatform(); + MetaWearablesDatMockDevicePlatform.instance = fake; - final uuid = await MetaWearablesDatMockDevice.pairGlasses( - model: GlassesModel.oakleyMetaHSTN, - ); + final uuid = await MetaWearablesDatMockDevice.pairGlasses( + model: GlassesModel.oakleyMetaHSTN, + ); - expect(fake.lastModel, GlassesModel.oakleyMetaHSTN); - expect(uuid, 'fake-uuid'); - }); + expect(fake.lastModel, GlassesModel.oakleyMetaHSTN); + expect(uuid, 'fake-uuid'); + }, + ); } class _FakeMockPlatform extends MetaWearablesDatMockDevicePlatform { diff --git a/lib/flutter_meta_wearables_dat.dart b/lib/flutter_meta_wearables_dat.dart index dd8b5d7..21d0c7e 100644 --- a/lib/flutter_meta_wearables_dat.dart +++ b/lib/flutter_meta_wearables_dat.dart @@ -798,9 +798,7 @@ class MetaWearablesDat { FrameFormat.rawStraightRgba => ui.ImageByteFormat.rawStraightRgba, FrameFormat.png => ui.ImageByteFormat.png, }; - final byteData = await image.toByteData( - format: imageByteFormat, - ); + final byteData = await image.toByteData(format: imageByteFormat); if (byteData == null) return null; return CapturedFrame( bytes: byteData.buffer.asUint8List(), diff --git a/lib/meta_wearables_dat_method_channel.dart b/lib/meta_wearables_dat_method_channel.dart index 3a2aa7a..4bb65ee 100644 --- a/lib/meta_wearables_dat_method_channel.dart +++ b/lib/meta_wearables_dat_method_channel.dart @@ -166,9 +166,7 @@ class MethodChannelMetaWearablesDat extends MetaWearablesDatPlatform { String? deviceId, { PhotoCaptureFormat format = PhotoCaptureFormat.jpeg, }) async { - final args = { - 'format': format.value, - }; + final args = {'format': format.value}; if (deviceId != null) { args['deviceId'] = deviceId; } @@ -318,24 +316,24 @@ class MethodChannelMetaWearablesDat extends MetaWearablesDatPlatform { @override Stream videoFramesStream() { - return videoFramesEventChannel.receiveBroadcastStream().map( - (dynamic event) { - final map = Map.from(event as Map); - final codecStr = map['codec'] as String? ?? 'raw'; - final codec = codecStr == 'hvc1' ? VideoCodec.hvc1 : VideoCodec.raw; - final bytes = map['bytes'] as Uint8List; - final bytesPerRow = (map['bytesPerRow'] as num?)?.toInt(); - return VideoFrame( - codec: codec, - bytes: bytes, - width: (map['width'] as num).toInt(), - height: (map['height'] as num).toInt(), - presentationTimestampUs: (map['ptsUs'] as num).toInt(), - isKeyframe: (map['isKeyframe'] as bool?) ?? true, - bytesPerRow: bytesPerRow, - ); - }, - ); + return videoFramesEventChannel.receiveBroadcastStream().map(( + dynamic event, + ) { + final map = Map.from(event as Map); + final codecStr = map['codec'] as String? ?? 'raw'; + final codec = codecStr == 'hvc1' ? VideoCodec.hvc1 : VideoCodec.raw; + final bytes = map['bytes'] as Uint8List; + final bytesPerRow = (map['bytesPerRow'] as num?)?.toInt(); + return VideoFrame( + codec: codec, + bytes: bytes, + width: (map['width'] as num).toInt(), + height: (map['height'] as num).toInt(), + presentationTimestampUs: (map['ptsUs'] as num).toInt(), + isKeyframe: (map['isKeyframe'] as bool?) ?? true, + bytesPerRow: bytesPerRow, + ); + }); } @override diff --git a/lib/meta_wearables_dat_platform_interface.dart b/lib/meta_wearables_dat_platform_interface.dart index e07507b..fbf44a0 100644 --- a/lib/meta_wearables_dat_platform_interface.dart +++ b/lib/meta_wearables_dat_platform_interface.dart @@ -104,9 +104,7 @@ abstract class MetaWearablesDatPlatform extends PlatformInterface { } Stream activeDeviceStream() { - throw UnimplementedError( - 'activeDeviceStream() has not been implemented.', - ); + throw UnimplementedError('activeDeviceStream() has not been implemented.'); } Future> getDevices() {