Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shorebird_cli): check for available upgrade after command run #2184

Merged
merged 5 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/shorebird_cli/lib/src/executables/git.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,24 @@ class Git {
final result = await git(['status', ...?args], workingDirectory: directory);
return '${result.stdout}'.trim();
}

/// The output of `git symbolic-ref [revision]` for the git repository located
/// in [directory]. If not provided, [revision] defaults to 'HEAD'.
Future<String> symbolicRef({
required Directory directory,
String revision = 'HEAD',
}) async {
final result = await git(
['symbolic-ref', revision],
workingDirectory: directory.path,
);
return '${result.stdout}'.trim();
}

/// Returns the name of the branch the git repository located at [directory]
/// is currently on.
Future<String> currentBranch({required Directory directory}) async {
return (await symbolicRef(directory: directory))
.replaceAll('refs/heads/', '');
}
}
33 changes: 21 additions & 12 deletions packages/shorebird_cli/lib/src/shorebird_cli_command_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,6 @@ ${lightCyan.wrap('shorebird release android -- --no-pub lib/main.dart')}''';
Shorebird $packageVersion • git@github.com:shorebirdtech/shorebird.git
$shorebirdFlutterPrefix • revision ${shorebirdEnv.flutterRevision}
Engine • revision ${shorebirdEnv.shorebirdEngineRevision}''');

try {
final isUpToDate = await shorebirdVersion.isLatest();
if (!isUpToDate) {
logger.info('''
A new version of shorebird is available!
Run ${lightCyan.wrap('shorebird upgrade')} to upgrade.''');
}
} catch (error) {
logger.detail('Unable to check for updates.\n$error');
}
exitCode = ExitCode.success.code;
} else {
try {
exitCode = await super.runCommand(topLevelResults);
Expand Down Expand Up @@ -233,6 +221,10 @@ ${currentRunLogFile.absolute.path}
);
}

if (topLevelResults.command?.name != UpgradeCommand.commandName) {
await _checkForUpdates();
}

return exitCode;
}

Expand All @@ -244,4 +236,21 @@ ${currentRunLogFile.absolute.path}
return null;
}
}

/// If this version of shorebird is on the `stable` branch, checks to see if
/// there are newer commits available. If there are, prints a message to the
/// user telling them to run `shorebird upgrade`.
Future<void> _checkForUpdates() async {
try {
if (await shorebirdVersion.isTrackingStable() &&
!await shorebirdVersion.isLatest()) {
logger
..info('')
..info('A new version of shorebird is available!')
..info('Run ${lightCyan.wrap('shorebird upgrade')} to upgrade.');
}
} catch (error) {
logger.detail('Unable to check for updates.\n$error');
}
}
}
8 changes: 8 additions & 0 deletions packages/shorebird_cli/lib/src/shorebird_version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,12 @@ class ShorebirdVersion {
args: ['--hard'],
);
}

/// Whether the current version of shorebird is tracking the stable branch. If
/// we are not tracking stable, we should not check for newer versions of
/// shorebird or try to auto-update.
Future<bool> isTrackingStable() async {
return (await git.currentBranch(directory: Directory(_workingDirectory))) ==
'stable';
}
}
4 changes: 2 additions & 2 deletions packages/shorebird_cli/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -378,10 +378,10 @@ packages:
dependency: "direct main"
description:
name: json_path
sha256: "149d32ceb7dc22422ea6d09e401fd688f54e1343bc9ff8c3cb1900ca3b1ad8b1"
sha256: dc25b4e2297a6bd39fb52b7d122a7787b7dab751fb278d315b54706b98bb76db
url: "https://pub.dev"
source: hosted
version: "0.7.1"
version: "0.7.2"
json_serializable:
dependency: "direct dev"
description:
Expand Down
56 changes: 56 additions & 0 deletions packages/shorebird_cli/test/src/executables/git_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -460,5 +460,61 @@ origin/flutter_release/3.10.6''';
);
});
});

group('symbolicRef', () {
setUp(() {
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
when(() => processResult.stdout).thenReturn('refs/heads/main');
});

test('executes correct command', () async {
final directory = Directory.current;
await expectLater(
runWithOverrides(
() => git.symbolicRef(directory: directory, revision: '1234'),
),
completion(equals('refs/heads/main')),
);
verify(
() => process.run(
'git',
['symbolic-ref', '1234'],
workingDirectory: directory.path,
),
).called(1);
});

test('defaults to HEAD if no revision is provided', () async {
final directory = Directory.current;
await expectLater(
runWithOverrides(() => git.symbolicRef(directory: directory)),
completion(equals('refs/heads/main')),
);
verify(
() => process.run(
'git',
['symbolic-ref', 'HEAD'],
workingDirectory: directory.path,
),
).called(1);
});
});

group('currentBranch', () {
setUp(() {
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
when(() => processResult.stdout).thenReturn('''
refs/heads/main
''');
});

test('removes refs/heads from branch name', () async {
final directory = Directory.current;
await expectLater(
runWithOverrides(() => git.currentBranch(directory: directory)),
completion(equals('main')),
);
});
});
});
}
161 changes: 107 additions & 54 deletions packages/shorebird_cli/test/src/shorebird_cli_command_runner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ void main() {
when(
() => shorebirdFlutter.getVersionString(),
).thenAnswer((_) async => flutterVersion);
when(() => shorebirdVersion.isLatest()).thenAnswer((_) async => true);
when(shorebirdVersion.isLatest).thenAnswer((_) async => true);
when(shorebirdVersion.isTrackingStable).thenAnswer((_) async => true);
commandRunner = runWithOverrides(ShorebirdCliCommandRunner.new);
});

Expand Down Expand Up @@ -176,59 +177,6 @@ Engine • revision $shorebirdEngineRevision''',
),
).called(1);
});

test('gracefully handles case when flutter version cannot be determined',
() async {
when(() => shorebirdFlutter.getVersionString()).thenThrow('error');
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));
verify(
() => logger.info(
'''
Shorebird $packageVersion • git@github.com:shorebirdtech/shorebird.git
Flutter • revision $flutterRevision
Engine • revision $shorebirdEngineRevision''',
),
).called(1);
verify(
() => logger.detail('Unable to determine Flutter version.\nerror'),
).called(1);
});

test('gracefully handles case when latest version cannot be determined',
() async {
when(() => shorebirdVersion.isLatest()).thenThrow('error');
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));
verify(
() => logger.info(
'''
Shorebird $packageVersion • git@github.com:shorebirdtech/shorebird.git
Flutter $flutterVersion • revision $flutterRevision
Engine • revision $shorebirdEngineRevision''',
),
).called(1);
verify(
() => logger.detail('Unable to check for updates.\nerror'),
).called(1);
});

test('logs update message when update is available', () async {
when(() => shorebirdVersion.isLatest()).thenAnswer((_) async => false);
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));
verify(
() => logger.info('''
A new version of shorebird is available!
Run ${lightCyan.wrap('shorebird upgrade')} to upgrade.'''),
).called(1);
});
});

group('--verbose', () {
Expand Down Expand Up @@ -329,5 +277,110 @@ Run ${lightCyan.wrap('shorebird upgrade')} to upgrade.'''),
expect(result, equals(ExitCode.success.code));
});
});

group('update check', () {
group('when running upgrade command', () {
setUp(() {
when(() => logger.progress(any())).thenReturn(MockProgress());
when(shorebirdVersion.fetchCurrentGitHash)
.thenAnswer((_) async => 'current');
when(shorebirdVersion.fetchLatestGitHash)
.thenAnswer((_) async => 'current');
});
test('does not check for update', () async {
final result = await runWithOverrides(
() => commandRunner.run(['upgrade']),
);
expect(result, equals(ExitCode.success.code));
verifyNever(() => shorebirdVersion.isTrackingStable());
verifyNever(() => shorebirdVersion.isLatest());
});
});

group('when tracking the stable branch', () {
setUp(() {
when(shorebirdVersion.isTrackingStable).thenAnswer((_) async => true);
});

test('gracefully handles case when latest version cannot be determined',
() async {
when(shorebirdVersion.isLatest).thenThrow('error');
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));
verify(
() => logger.detail('Unable to check for updates.\nerror'),
).called(1);
});

group('when update is available', () {
test('logs update message', () async {
when(shorebirdVersion.isLatest).thenAnswer((_) async => false);
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
verify(
() => logger.info('A new version of shorebird is available!'),
).called(1);
verify(
() => logger.info(
'Run ${lightCyan.wrap('shorebird upgrade')} to upgrade.'),
).called(1);

expect(result, equals(ExitCode.success.code));
});
});

group('when no update is available', () {
setUp(() {
when(shorebirdVersion.isLatest).thenAnswer((_) async => true);
});

test('does not log update message', () async {
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));
verifyNever(
() => logger.info('A new version of shorebird is available!'),
);
});
});

test(
'gracefully handles case when flutter version cannot be determined',
() async {
when(shorebirdFlutter.getVersionString).thenThrow('error');
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));
verify(
() => logger.detail('Unable to determine Flutter version.\nerror'),
).called(1);
});
});

group('when not tracking the stable branch', () {
setUp(() {
when(shorebirdVersion.isTrackingStable)
.thenAnswer((_) async => false);
when(shorebirdVersion.isLatest).thenAnswer((_) async => false);
});

test('does not check for updates or print update message', () async {
final result = await runWithOverrides(
() => commandRunner.run(['--version']),
);
expect(result, equals(ExitCode.success.code));

verifyNever(shorebirdVersion.isLatest);
verifyNever(
() => logger.info('A new version of shorebird is available!'),
);
});
});
});
});
}
34 changes: 34 additions & 0 deletions packages/shorebird_cli/test/src/shorebird_version_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ void main() {
);
}

setUpAll(() {
registerFallbackValue(Directory(''));
});

setUp(() {
git = MockGit();
shorebirdVersionManager = ShorebirdVersion();
Expand Down Expand Up @@ -196,5 +200,35 @@ void main() {
);
});
});

group('isTrackingStable', () {
group('when on the stable branch', () {
setUp(() {
when(() => git.currentBranch(directory: any(named: 'directory')))
.thenAnswer((_) async => 'stable');
});

test('returns true', () async {
expect(
await runWithOverrides(shorebirdVersionManager.isTrackingStable),
isTrue,
);
});
});

group('when on a branch other than stable', () {
setUp(() {
when(() => git.currentBranch(directory: any(named: 'directory')))
.thenAnswer((_) async => 'main');
});

test('returns false', () async {
expect(
await runWithOverrides(shorebirdVersionManager.isTrackingStable),
isFalse,
);
});
});
});
});
}
Loading