diff --git a/packages/shorebird_cli/lib/src/git.dart b/packages/shorebird_cli/lib/src/git.dart index 460245b16..88efa1aac 100644 --- a/packages/shorebird_cli/lib/src/git.dart +++ b/packages/shorebird_cli/lib/src/git.dart @@ -87,6 +87,30 @@ class Git { } } + /// Iterate over all refs that match [pattern] and show them + /// according to the given [format]. + Future forEachRef({ + required String directory, + required String format, + required String pattern, + }) async { + final arguments = ['for-each-ref', '--format', format, pattern]; + final result = await process.run( + executable, + arguments, + workingDirectory: directory, + ); + if (result.exitCode != 0) { + throw ProcessException( + executable, + arguments, + '${result.stderr}', + result.exitCode, + ); + } + return '${result.stdout}'.trim(); + } + /// Prunes stale remote branches from the repository at [directory] /// associated with [name]. Future remotePrune({ diff --git a/packages/shorebird_cli/test/src/git_test.dart b/packages/shorebird_cli/test/src/git_test.dart index 16d1746f8..af54136ce 100644 --- a/packages/shorebird_cli/test/src/git_test.dart +++ b/packages/shorebird_cli/test/src/git_test.dart @@ -189,6 +189,59 @@ void main() { }); }); + group('forEachRef', () { + const directory = 'repository'; + const format = '%(refname:short)'; + const pattern = 'refs/remotes/origin/flutter_release/*'; + const output = ''' + +origin/flutter_release/3.10.0 +origin/flutter_release/3.10.1 +origin/flutter_release/3.10.2 +origin/flutter_release/3.10.3 +origin/flutter_release/3.10.4 +origin/flutter_release/3.10.5 +origin/flutter_release/3.10.6'''; + test('executes correct command', () async { + when(() => processResult.stdout).thenReturn(output); + await expectLater( + runWithOverrides( + () => git.forEachRef( + pattern: pattern, + format: format, + directory: directory, + ), + ), + completion(equals(output.trim())), + ); + verify( + () => process.run( + 'git', + ['for-each-ref', '--format', format, pattern], + workingDirectory: directory, + ), + ).called(1); + }); + + test('throws ProcessException if process exits with error', () async { + const error = 'oops'; + when(() => processResult.exitCode).thenReturn(ExitCode.software.code); + when(() => processResult.stderr).thenReturn(error); + expect( + () => runWithOverrides( + () => git.forEachRef( + pattern: pattern, + format: format, + directory: directory, + ), + ), + throwsA( + isA().having((e) => e.message, 'message', error), + ), + ); + }); + }); + group('remotePrune', () { const directory = './output'; const name = 'origin';