diff --git a/bin/internal/flutter.version b/bin/internal/flutter.version index 142383be..15d6a4ed 100644 --- a/bin/internal/flutter.version +++ b/bin/internal/flutter.version @@ -1 +1 @@ -cd234ef8ece6e82da6bec9b9d811e818e8d8373a +c5176e1323bd85e34758ae546d6f2b8003d3109c diff --git a/packages/shorebird_cli/lib/src/commands/release/releaser.dart b/packages/shorebird_cli/lib/src/commands/release/releaser.dart index 2fc4b3b6..d48eae2b 100644 --- a/packages/shorebird_cli/lib/src/commands/release/releaser.dart +++ b/packages/shorebird_cli/lib/src/commands/release/releaser.dart @@ -153,11 +153,30 @@ abstract class Releaser { platformSubdir: supplementPlatformSubdir, create: hasObfuscationMap, ); - if (hasObfuscationMap && supplementDir != null) { - obfuscationMapFile.copySync( - p.join(supplementDir.path, 'obfuscation_map.json'), + if (supplementDir == null) return null; + + final targetMapFile = File( + p.join(supplementDir.path, 'obfuscation_map.json'), + ); + if (hasObfuscationMap) { + obfuscationMapFile.copySync(targetMapFile.path); + } else if (targetMapFile.existsSync()) { + // Remove stale obfuscation map from a previous obfuscated build so that + // the supplement doesn't incorrectly signal that this release was + // obfuscated. + logger.detail( + 'Removing stale obfuscation map from supplement directory. ' + 'If this release should be obfuscated, re-run with --obfuscate.', ); + targetMapFile.deleteSync(); } + + // Return null if the supplement directory is now empty (nothing to upload). + // Note: currently the obfuscation map is the only supplement artifact. If + // other supplement files are added in the future, this check (and the + // stale-file cleanup above) should be updated to handle them explicitly. + if (supplementDir.listSync().isEmpty) return null; + return supplementDir; } diff --git a/packages/shorebird_cli/test/src/commands/release/aar_releaser_test.dart b/packages/shorebird_cli/test/src/commands/release/aar_releaser_test.dart index 2867e377..e73b9655 100644 --- a/packages/shorebird_cli/test/src/commands/release/aar_releaser_test.dart +++ b/packages/shorebird_cli/test/src/commands/release/aar_releaser_test.dart @@ -562,6 +562,121 @@ void main() { }); }); + group('assembleSupplementDirectory', () { + late Directory supplementDir; + + setUp(() { + // Create the supplement directory as it would exist on disk. + supplementDir = Directory( + p.join(projectRoot.path, 'build', 'android', 'shorebird'), + )..createSync(recursive: true); + + // Stub getReleaseSupplementDirectory to use the real directory. + when( + () => artifactManager.getReleaseSupplementDirectory( + platformSubdir: any(named: 'platformSubdir'), + create: any(named: 'create'), + ), + ).thenAnswer((invocation) { + final create = + invocation.namedArguments[const Symbol('create')] as bool; + if (!supplementDir.existsSync() && create) { + supplementDir.createSync(recursive: true); + } + return supplementDir.existsSync() ? supplementDir : null; + }); + }); + + group('when obfuscation is enabled and map exists', () { + setUp(() { + when(() => argResults['obfuscate']).thenReturn(true); + when(() => argResults.wasParsed('obfuscate')).thenReturn(true); + + // Create the obfuscation map at the expected build output location. + final mapFile = File( + p.join( + projectRoot.path, + 'build', + 'shorebird', + 'obfuscation_map.json', + ), + )..createSync(recursive: true); + mapFile.writeAsStringSync('{"key": "value"}'); + }); + + test('copies map into supplement directory and returns it', () { + final result = runWithOverrides( + () => aarReleaser.assembleSupplementDirectory(), + ); + + expect(result, isNotNull); + final mapInSupplement = File( + p.join(supplementDir.path, 'obfuscation_map.json'), + ); + expect(mapInSupplement.existsSync(), isTrue); + expect(mapInSupplement.readAsStringSync(), '{"key": "value"}'); + }); + }); + + group('when obfuscation is disabled', () { + setUp(() { + when(() => argResults['obfuscate']).thenReturn(false); + }); + + group('and no stale map exists', () { + test('returns null', () { + // Remove the supplement dir so it's empty/missing. + if (supplementDir.existsSync()) { + supplementDir.deleteSync(recursive: true); + } + when( + () => artifactManager.getReleaseSupplementDirectory( + platformSubdir: any(named: 'platformSubdir'), + create: any(named: 'create'), + ), + ).thenReturn(null); + + final result = runWithOverrides( + () => aarReleaser.assembleSupplementDirectory(), + ); + + expect(result, isNull); + }); + }); + + group('and a stale obfuscation map exists', () { + setUp(() { + // Simulate a leftover obfuscation_map.json from a previous + // obfuscated build. + File( + p.join(supplementDir.path, 'obfuscation_map.json'), + ) + ..createSync(recursive: true) + ..writeAsStringSync('{"stale": true}'); + }); + + test('removes stale map, logs detail, and returns null', () { + final result = runWithOverrides( + () => aarReleaser.assembleSupplementDirectory(), + ); + + expect(result, isNull); + expect( + File( + p.join(supplementDir.path, 'obfuscation_map.json'), + ).existsSync(), + isFalse, + ); + verify( + () => logger.detail( + any(that: contains('Removing stale obfuscation map')), + ), + ).called(1); + }); + }); + }); + }); + group('getReleaseVersion', () { const releaseVersion = '1.0.0'; setUp(() {