feat(shorebird_cli): handle missing shorebird.yaml file gracefully when promoting a patch (#3349)

Co-authored-by: Bryan Oltman <bryan@shorebird.dev>
This commit is contained in:
Peter Trost
2025-10-09 20:27:52 +02:00
committed by GitHub
parent ec859e2447
commit a4525ad292
2 changed files with 43 additions and 0 deletions
@@ -7,6 +7,7 @@ import 'package:shorebird_cli/src/extensions/arg_results.dart';
import 'package:shorebird_cli/src/logging/logging.dart';
import 'package:shorebird_cli/src/shorebird_command.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
/// {@template promote_command}
/// Promotes a patch to the production channel.
@@ -39,6 +40,15 @@ class PromoteCommand extends ShorebirdCommand {
@override
Future<int> run() async {
try {
await shorebirdValidator.validatePreconditions(
checkUserIsAuthenticated: true,
checkShorebirdInitialized: true,
);
} on PreconditionFailedException catch (error) {
return error.exitCode.code;
}
final releaseVersion = results['release-version'] as String;
final patchNumber = int.parse(results['patch-number'] as String);
final flavor = results.findOption('flavor', argParser: argParser);
@@ -8,6 +8,7 @@ import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/deployment_track.dart';
import 'package:shorebird_cli/src/logging/logging.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
import 'package:test/test.dart';
@@ -49,6 +50,7 @@ void main() {
late ArgResults argResults;
late CodePushClientWrapper codePushClientWrapper;
late ShorebirdEnv shorebirdEnv;
late ShorebirdValidator shorebirdValidator;
late ShorebirdLogger logger;
late PromoteCommand command;
@@ -60,6 +62,7 @@ void main() {
codePushClientWrapperRef.overrideWith(() => codePushClientWrapper),
loggerRef.overrideWith(() => logger),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
shorebirdValidatorRef.overrideWith(() => shorebirdValidator),
},
);
}
@@ -73,6 +76,7 @@ void main() {
codePushClientWrapper = MockCodePushClientWrapper();
logger = MockShorebirdLogger();
shorebirdEnv = MockShorebirdEnv();
shorebirdValidator = MockShorebirdValidator();
when(() => argResults.wasParsed(any())).thenReturn(false);
when(() => argResults.rest).thenReturn([]);
@@ -107,6 +111,13 @@ void main() {
when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml);
when(
() => shorebirdValidator.validatePreconditions(
checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'),
checkShorebirdInitialized: any(named: 'checkShorebirdInitialized'),
),
).thenAnswer((_) async {});
command = PromoteCommand()..testArgResults = argResults;
});
@@ -114,6 +125,28 @@ void main() {
expect(command.description, isNotEmpty);
});
group('when validation fails', () {
final exception = ShorebirdNotInitializedException();
setUp(() {
when(
() => shorebirdValidator.validatePreconditions(
checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'),
checkShorebirdInitialized: any(named: 'checkShorebirdInitialized'),
),
).thenThrow(exception);
});
test('exits with exit code from validation error', () async {
final result = await runWithOverrides(command.run);
expect(result, equals(exception.exitCode.code));
verify(
() => shorebirdValidator.validatePreconditions(
checkUserIsAuthenticated: true,
checkShorebirdInitialized: true,
),
).called(1);
});
});
group('when an invalid patch number is provided', () {
setUp(() {
when(() => argResults['patch-number']).thenReturn('5');