Skip to content

Commit afce55c

Browse files
authored
feat: add update command (#435)
1 parent 991e138 commit afce55c

File tree

9 files changed

+174
-26
lines changed

9 files changed

+174
-26
lines changed

lib/src/command_runner.dart

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class VeryGoodCommandRunner extends CommandRunner<int> {
4747
addCommand(CreateCommand(analytics: _analytics, logger: logger));
4848
addCommand(PackagesCommand(logger: logger));
4949
addCommand(TestCommand(logger: logger));
50+
addCommand(UpdateCommand(logger: logger, pubUpdater: pubUpdater));
5051
}
5152

5253
/// Standard timeout duration for the CLI.

lib/src/commands/commands.dart

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export 'create/create.dart';
22
export 'packages.dart';
33
export 'test/test.dart';
4+
export 'update.dart';

lib/src/commands/update.dart

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:args/command_runner.dart';
2+
import 'package:mason/mason.dart' hide packageVersion;
3+
import 'package:pub_updater/pub_updater.dart';
4+
import 'package:very_good_cli/src/command_runner.dart';
5+
import 'package:very_good_cli/src/version.dart';
6+
7+
/// {@template update_command}
8+
/// `very_good update` command which updates very_good cli.
9+
/// {@endtemplate}
10+
class UpdateCommand extends Command<int> {
11+
/// {@macro update_command}
12+
UpdateCommand({
13+
Logger? logger,
14+
PubUpdater? pubUpdater,
15+
}) : _logger = logger ?? Logger(),
16+
_pubUpdater = pubUpdater ?? PubUpdater();
17+
18+
final Logger _logger;
19+
final PubUpdater _pubUpdater;
20+
21+
@override
22+
String get description => 'Update Very Good CLI.';
23+
24+
@override
25+
String get name => 'update';
26+
27+
@override
28+
Future<int> run() async {
29+
final updateCheckProgress = _logger.progress('Checking for updates');
30+
late final String latestVersion;
31+
try {
32+
latestVersion = await _pubUpdater.getLatestVersion(packageName);
33+
} catch (error) {
34+
updateCheckProgress.fail();
35+
_logger.err('$error');
36+
return ExitCode.software.code;
37+
}
38+
updateCheckProgress.complete('Checked for updates');
39+
40+
final isUpToDate = packageVersion == latestVersion;
41+
if (isUpToDate) {
42+
_logger.info('Very Good CLI is already at the latest version.');
43+
return ExitCode.success.code;
44+
}
45+
46+
final updateProgress = _logger.progress('Updating to $latestVersion');
47+
try {
48+
await _pubUpdater.update(packageName: packageName);
49+
} catch (error) {
50+
updateProgress.fail();
51+
_logger.err('$error');
52+
return ExitCode.software.code;
53+
}
54+
updateProgress.complete('Updated to $latestVersion');
55+
56+
return ExitCode.success.code;
57+
}
58+
}

test/helpers/command_helper.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ void Function() withRunner(
3333
FutureOr<void> Function(
3434
VeryGoodCommandRunner commandRunner,
3535
Logger logger,
36+
PubUpdater pubUpdater,
3637
List<String> printLogs,
3738
)
3839
runnerFn,
@@ -69,6 +70,6 @@ void Function() withRunner(
6970
),
7071
).thenAnswer((_) => Future.value(true));
7172

72-
await runnerFn(commandRunner, logger, printLogs);
73+
await runnerFn(commandRunner, logger, pubUpdater, printLogs);
7374
});
7475
}

test/src/command_runner_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const expectedUsage = [
3434
''' Creates a new very good project in the specified directory.\n'''
3535
' packages Command for managing packages.\n'
3636
' test Run tests in a Dart or Flutter project.\n'
37+
' update Update Very Good CLI.\n'
3738
'\n'
3839
'Run "very_good help <command>" for more information about a command.'
3940
];

test/src/commands/create/create_test.dart

+12-12
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ void main() {
115115

116116
test(
117117
'help',
118-
withRunner((commandRunner, logger, printLogs) async {
118+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
119119
final result = await commandRunner.run(['create', '--help']);
120120
expect(printLogs, equals(expectedUsage));
121121
expect(result, equals(ExitCode.success.code));
@@ -136,7 +136,7 @@ void main() {
136136
test(
137137
'throws UsageException when --project-name is missing '
138138
'and directory base is not a valid package name',
139-
withRunner((commandRunner, logger, printLogs) async {
139+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
140140
const expectedErrorMessage = '".tmp" is not a valid package name.\n\n'
141141
'See https://dart.dev/tools/pub/pubspec#name for more information.';
142142
final result = await commandRunner.run(['create', '.tmp']);
@@ -147,7 +147,7 @@ void main() {
147147

148148
test(
149149
'throws UsageException when --project-name is invalid',
150-
withRunner((commandRunner, logger, printLogs) async {
150+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
151151
const expectedErrorMessage = '"My App" is not a valid package name.\n\n'
152152
'See https://dart.dev/tools/pub/pubspec#name for more information.';
153153
final result = await commandRunner.run(
@@ -160,7 +160,7 @@ void main() {
160160

161161
test(
162162
'throws UsageException when output directory is missing',
163-
withRunner((commandRunner, logger, printLogs) async {
163+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
164164
const expectedErrorMessage =
165165
'No option specified for the output directory.';
166166
final result = await commandRunner.run(['create']);
@@ -171,7 +171,7 @@ void main() {
171171

172172
test(
173173
'throws UsageException when multiple output directories are provided',
174-
withRunner((commandRunner, logger, printLogs) async {
174+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
175175
const expectedErrorMessage = 'Multiple output directories specified.';
176176
final result = await commandRunner.run(['create', './a', './b']);
177177
expect(result, equals(ExitCode.usage.code));
@@ -322,7 +322,7 @@ void main() {
322322
test(
323323
'is a valid alias',
324324
withRunner(
325-
(commandRunner, logger, printLogs) async {
325+
(commandRunner, logger, pubUpdater, printLogs) async {
326326
const orgName = 'com.my.org';
327327
final tempDir = Directory.systemTemp.createTempSync();
328328
final result = await commandRunner.run(
@@ -347,7 +347,7 @@ void main() {
347347

348348
test(
349349
'no delimiters',
350-
withRunner((commandRunner, logger, printLogs) async {
350+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
351351
const orgName = 'My App';
352352
final result = await commandRunner.run(
353353
['create', '.', '--org-name', orgName],
@@ -359,7 +359,7 @@ void main() {
359359

360360
test(
361361
'less than 2 domains',
362-
withRunner((commandRunner, logger, printLogs) async {
362+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
363363
const orgName = 'verybadtest';
364364
final result = await commandRunner.run(
365365
['create', '.', '--org-name', orgName],
@@ -371,7 +371,7 @@ void main() {
371371

372372
test(
373373
'invalid characters present',
374-
withRunner((commandRunner, logger, printLogs) async {
374+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
375375
const orgName = 'very%.bad@.#test';
376376
final result = await commandRunner.run(
377377
['create', '.', '--org-name', orgName],
@@ -383,7 +383,7 @@ void main() {
383383

384384
test(
385385
'segment starts with a non-letter',
386-
withRunner((commandRunner, logger, printLogs) async {
386+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
387387
const orgName = 'very.bad.1test';
388388
final result = await commandRunner.run(
389389
['create', '.', '--org-name', orgName],
@@ -395,7 +395,7 @@ void main() {
395395

396396
test(
397397
'valid prefix but invalid suffix',
398-
withRunner((commandRunner, logger, printLogs) async {
398+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
399399
const orgName = 'very.good.prefix.bad@@suffix';
400400
final result = await commandRunner.run(
401401
['create', '.', '--org-name', orgName],
@@ -498,7 +498,7 @@ void main() {
498498
group('invalid template name', () {
499499
test(
500500
'invalid template name',
501-
withRunner((commandRunner, logger, printLogs) async {
501+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
502502
const templateName = 'badtemplate';
503503
const expectedErrorMessage =
504504
'''"$templateName" is not an allowed value for option "template".''';

test/src/commands/packages_test.dart

+10-10
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void main() {
4242
group('packages', () {
4343
test(
4444
'help',
45-
withRunner((commandRunner, logger, printLogs) async {
45+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
4646
final result = await commandRunner.run(['packages', '--help']);
4747
expect(printLogs, equals(expectedPackagesUsage));
4848
expect(result, equals(ExitCode.success.code));
@@ -58,7 +58,7 @@ void main() {
5858
group('get', () {
5959
test(
6060
'help',
61-
withRunner((commandRunner, logger, printLogs) async {
61+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
6262
final result = await commandRunner.run(['packages', 'get', '--help']);
6363
expect(printLogs, equals(expectedPackagesGetUsage));
6464
expect(result, equals(ExitCode.success.code));
@@ -74,7 +74,7 @@ void main() {
7474
test(
7575
'throws usage exception '
7676
'when too many arguments are provided',
77-
withRunner((commandRunner, logger, printLogs) async {
77+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
7878
final result = await commandRunner.run(
7979
['packages', 'get', 'arg1', 'arg2'],
8080
);
@@ -85,7 +85,7 @@ void main() {
8585
test(
8686
'throws pubspec not found exception '
8787
'when no pubspec.yaml exists',
88-
withRunner((commandRunner, logger, printLogs) async {
88+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
8989
final result = await commandRunner.run(['packages', 'get', 'test']);
9090
expect(result, equals(ExitCode.noInput.code));
9191
verify(() {
@@ -97,7 +97,7 @@ void main() {
9797
test(
9898
'throws pubspec not found exception '
9999
'when no pubspec.yaml exists (recursive)',
100-
withRunner((commandRunner, logger, printLogs) async {
100+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
101101
final result = await commandRunner.run(
102102
['packages', 'get', '-r', 'test'],
103103
);
@@ -110,7 +110,7 @@ void main() {
110110

111111
test(
112112
'throws when installation fails',
113-
withRunner((commandRunner, logger, printLogs) async {
113+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
114114
final directory = Directory.systemTemp.createTempSync();
115115
File(path.join(directory.path, 'pubspec.yaml')).writeAsStringSync('');
116116
final result = await commandRunner.run(
@@ -122,7 +122,7 @@ void main() {
122122

123123
test(
124124
'ignores .fvm directory',
125-
withRunner((commandRunner, logger, printLogs) async {
125+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
126126
final tempDirectory = Directory.systemTemp.createTempSync();
127127
final directory = Directory(path.join(tempDirectory.path, '.fvm'))
128128
..createSync();
@@ -148,7 +148,7 @@ void main() {
148148
test(
149149
'completes normally '
150150
'when pubspec.yaml exists',
151-
withRunner((commandRunner, logger, printLogs) async {
151+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
152152
final directory = Directory.systemTemp.createTempSync();
153153
File(path.join(directory.path, 'pubspec.yaml')).writeAsStringSync(
154154
'''
@@ -174,7 +174,7 @@ void main() {
174174
test(
175175
'completes normally '
176176
'when pubspec.yaml exists (recursive)',
177-
withRunner((commandRunner, logger, printLogs) async {
177+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
178178
final directory = Directory.systemTemp.createTempSync();
179179
final pubspecA = File(
180180
path.join(directory.path, 'example_a', 'pubspec.yaml'),
@@ -220,7 +220,7 @@ void main() {
220220
test(
221221
'completes normally '
222222
'when pubspec.yaml exists and directory is not ignored (recursive)',
223-
withRunner((commandRunner, logger, printLogs) async {
223+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
224224
final tempDirectory = Directory.systemTemp.createTempSync();
225225
final directory = Directory(
226226
path.join(tempDirectory.path, 'macos_plugin'),

test/src/commands/test/test_test.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void main() {
102102

103103
test(
104104
'help',
105-
withRunner((commandRunner, logger, printLogs) async {
105+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
106106
final result = await commandRunner.run(['test', '--help']);
107107
expect(printLogs, equals(expectedTestUsage));
108108
expect(result, equals(ExitCode.success.code));
@@ -118,7 +118,7 @@ void main() {
118118
test(
119119
'throws pubspec not found exception '
120120
'when no pubspec.yaml exists',
121-
withRunner((commandRunner, logger, printLogs) async {
121+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
122122
final directory = Directory.systemTemp.createTempSync();
123123
Directory.current = directory.path;
124124
final result = await commandRunner.run(['test']);
@@ -132,7 +132,7 @@ void main() {
132132
test(
133133
'throws pubspec not found exception '
134134
'when no pubspec.yaml exists (recursive)',
135-
withRunner((commandRunner, logger, printLogs) async {
135+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
136136
final directory = Directory.systemTemp.createTempSync();
137137
Directory.current = directory.path;
138138
final result = await commandRunner.run(['test', '-r']);

0 commit comments

Comments
 (0)