Skip to content

Commit

Permalink
feat(shorebird_cli): check for available upgrade after command run (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanoltman authored Jun 3, 2024
1 parent 6fb9f2a commit e0ace35
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 68 deletions.
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,
);
});
});
});
});
}

0 comments on commit e0ace35

Please sign in to comment.