Skip to content

Commit 2e1d2f9

Browse files
committed
refactor: move config load to VeryGoodConfig
1 parent dbe955c commit 2e1d2f9

15 files changed

Lines changed: 416 additions & 77 deletions

File tree

lib/src/commands/create/commands/create_subcommand.dart

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -207,17 +207,9 @@ abstract class CreateSubCommand extends Command<int> {
207207

208208
@override
209209
Future<int> run() async {
210-
try {
211-
createConfig = VeryGoodConfig.loadFromClosestAncestor(
212-
Directory.current,
213-
).create;
214-
} on VeryGoodConfigParseException catch (e) {
215-
logger.err(
216-
'Could not read `$veryGoodConfigFileName`.\n'
217-
'${e.message}',
218-
);
219-
return ExitCode.config.code;
220-
}
210+
final config = VeryGoodConfig.load(Directory.current, logger: logger);
211+
if (config == null) return ExitCode.config.code;
212+
createConfig = config.create;
221213

222214
final template = this.template;
223215
final bundle = template.bundle;
@@ -381,5 +373,9 @@ mixin Publishable on CreateSubCommand {
381373
/// to the brick generator.
382374
mixin Workspace on CreateSubCommand {
383375
/// Gets the workspace flag.
384-
bool get workspace => argResults['workspace'] as bool? ?? false;
376+
bool get workspace => argResults.resolve<bool>(
377+
'workspace',
378+
createConfig.workspace,
379+
fallbackValue: false,
380+
);
385381
}

lib/src/commands/dart/commands/dart_test_command.dart

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -369,16 +369,8 @@ This command should be run from the root of your Dart project.''');
369369
return ExitCode.noInput.code;
370370
}
371371

372-
final VeryGoodConfig config;
373-
try {
374-
config = VeryGoodConfig.loadFromClosestAncestor(Directory(targetPath));
375-
} on VeryGoodConfigParseException catch (e) {
376-
_logger.err(
377-
'Could not read `$veryGoodConfigFileName`.\n'
378-
'${e.message}',
379-
);
380-
return ExitCode.config.code;
381-
}
372+
final config = VeryGoodConfig.load(Directory(targetPath), logger: _logger);
373+
if (config == null) return ExitCode.config.code;
382374

383375
final isDartInstalled = await _dartInstalled(logger: _logger);
384376

lib/src/commands/packages/commands/check/commands/licenses.dart

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,8 @@ class PackagesCheckLicensesCommand extends Command<int> {
211211
final target = _argResults.rest.length == 1 ? _argResults.rest[0] : '.';
212212
final targetPath = path.normalize(Directory(target).absolute.path);
213213

214-
final VeryGoodConfig config;
215-
try {
216-
config = VeryGoodConfig.loadFromClosestAncestor(Directory(targetPath));
217-
} on VeryGoodConfigParseException catch (e) {
218-
_logger.err(
219-
'Could not read `$veryGoodConfigFileName`.\n'
220-
'${e.message}',
221-
);
222-
return ExitCode.config.code;
223-
}
214+
final config = VeryGoodConfig.load(Directory(targetPath), logger: _logger);
215+
if (config == null) return ExitCode.config.code;
224216

225217
final options = PackagesCheckLicensesOptions.parse(
226218
_argResults,

lib/src/commands/packages/commands/get.dart

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,8 @@ class PackagesGetCommand extends Command<int> {
8585
final target = _argResults.rest.length == 1 ? _argResults.rest[0] : '.';
8686
final targetPath = path.normalize(Directory(target).absolute.path);
8787

88-
final VeryGoodConfig config;
89-
try {
90-
config = VeryGoodConfig.loadFromClosestAncestor(Directory(targetPath));
91-
} on VeryGoodConfigParseException catch (e) {
92-
_logger.err(
93-
'Could not read `$veryGoodConfigFileName`.\n'
94-
'${e.message}',
95-
);
96-
return ExitCode.config.code;
97-
}
88+
final config = VeryGoodConfig.load(Directory(targetPath), logger: _logger);
89+
if (config == null) return ExitCode.config.code;
9890

9991
final options = PackagesGetOptions.parse(_argResults, config: config);
10092

lib/src/commands/test/test.dart

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -458,16 +458,8 @@ This command should be run from the root of your Flutter project.''');
458458
return ExitCode.noInput.code;
459459
}
460460

461-
final VeryGoodConfig config;
462-
try {
463-
config = VeryGoodConfig.loadFromClosestAncestor(Directory(targetPath));
464-
} on VeryGoodConfigParseException catch (e) {
465-
_logger.err(
466-
'Could not read `$veryGoodConfigFileName`.\n'
467-
'${e.message}',
468-
);
469-
return ExitCode.config.code;
470-
}
461+
final config = VeryGoodConfig.load(Directory(targetPath), logger: _logger);
462+
if (config == null) return ExitCode.config.code;
471463

472464
final isFlutterInstalled = await _flutterInstalled(logger: _logger);
473465

lib/src/mcp/mcp_server.dart

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,20 @@ Only one value can be selected.
328328
);
329329
}
330330

331+
/// The `create` subcommands that define the `--workspace` flag.
332+
///
333+
/// `docs_site` is intentionally excluded because it does not mix in the
334+
/// `Workspace` mixin and therefore rejects the flag.
335+
static const _workspaceSubcommands = {
336+
'app_ui_package',
337+
'flame_game',
338+
'flutter_app',
339+
'flutter_package',
340+
'flutter_plugin',
341+
'dart_cli',
342+
'dart_package',
343+
};
344+
331345
List<String> _parseCreate(Map<String, Object?> args) {
332346
final subcommand = args['subcommand']! as String;
333347
final name = args['name']! as String;
@@ -352,10 +366,12 @@ Only one value can be selected.
352366
if (args['publishable'] == true) {
353367
cliArgs.add('--publishable');
354368
}
355-
if (args['workspace'] == true) {
356-
cliArgs.add('--workspace');
357-
} else if (args['workspace'] == false) {
358-
cliArgs.add('--no-workspace');
369+
if (_workspaceSubcommands.contains(subcommand)) {
370+
if (args['workspace'] == true) {
371+
cliArgs.add('--workspace');
372+
} else if (args['workspace'] == false) {
373+
cliArgs.add('--no-workspace');
374+
}
359375
}
360376
if (args['executable-name'] != null) {
361377
cliArgs.addAll(['--executable-name', args['executable-name']! as String]);

lib/src/very_good_config/very_good_config.dart

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:args/args.dart';
1111
import 'package:checked_yaml/checked_yaml.dart';
1212
import 'package:equatable/equatable.dart';
1313
import 'package:json_annotation/json_annotation.dart';
14+
import 'package:mason_logger/mason_logger.dart';
1415
import 'package:path/path.dart' as path;
1516

1617
part 'very_good_config.g.dart';
@@ -127,6 +128,26 @@ class VeryGoodConfig extends Equatable {
127128
}
128129
}
129130

131+
/// Loads the closest [VeryGoodConfig] to [directory].
132+
///
133+
/// On a parse failure, logs a formatted error via [logger] and returns
134+
/// `null`, signalling that the caller should exit with a config error.
135+
///
136+
/// Centralizes the load-and-report contract shared by every command that
137+
/// reads `very_good.yaml`, so the error message and exit behavior stay
138+
/// consistent.
139+
static VeryGoodConfig? load(Directory directory, {required Logger logger}) {
140+
try {
141+
return VeryGoodConfig.loadFromClosestAncestor(directory);
142+
} on VeryGoodConfigParseException catch (e) {
143+
logger.err(
144+
'Could not read `$veryGoodConfigFileName`.\n'
145+
'${e.message}',
146+
);
147+
return null;
148+
}
149+
}
150+
130151
/// Loads a [VeryGoodConfig] from the configuration file directly inside
131152
/// [directory], or `null` when the file does not exist.
132153
///
@@ -180,6 +201,7 @@ class VeryGoodCreateConfig extends Equatable {
180201
this.orgName,
181202
this.publishable,
182203
this.template,
204+
this.workspace,
183205
});
184206

185207
/// Creates a [VeryGoodCreateConfig] from a decoded YAML/JSON [json] map.
@@ -199,8 +221,18 @@ class VeryGoodCreateConfig extends Equatable {
199221
/// The template used to generate the project.
200222
final String? template;
201223

224+
/// Whether the generated project should resolve its dependencies from a
225+
/// parent Pub workspace.
226+
final bool? workspace;
227+
202228
@override
203-
List<Object?> get props => [description, orgName, publishable, template];
229+
List<Object?> get props => [
230+
description,
231+
orgName,
232+
publishable,
233+
template,
234+
workspace,
235+
];
204236
}
205237

206238
/// {@template very_good_test_config}

lib/src/very_good_config/very_good_config.g.dart

Lines changed: 21 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/docs/commands/create.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ create:
7979
org_name: com.very.good
8080
publishable: true
8181
template: core
82+
workspace: true
8283
```
8384
8485
With the file above, running `very_good create flutter_app my_app` behaves the same as running `very_good create flutter_app my_app --desc 'A Very Good project.' --org-name com.very.good --publishable --template core`. You can still override any of these values on the command line, for example `very_good create flutter_app my_app --org-name com.example` to use a different org name for a single run.

test/src/commands/create/create_subcommand_test.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,55 @@ Run "runner help" to see global options.''';
12351235

12361236
expect(command.publishable, isTrue);
12371237
});
1238+
1239+
test('resolves the workspace flag from the config value', () {
1240+
final argResults = _MockArgResults();
1241+
when(() => argResults.wasParsed(any())).thenReturn(false);
1242+
when(() => argResults['workspace'] as bool?).thenReturn(null);
1243+
1244+
final command =
1245+
_TestCreateSubCommandWithWorkspace(
1246+
template: template,
1247+
logger: logger,
1248+
generatorFromBundle: null,
1249+
)
1250+
..argResultOverrides = argResults
1251+
..createConfig = const VeryGoodCreateConfig(workspace: true);
1252+
1253+
expect(command.workspace, isTrue);
1254+
});
1255+
1256+
test('prefers the CLI workspace flag over the config value', () {
1257+
final argResults = _MockArgResults();
1258+
when(() => argResults.wasParsed(any())).thenReturn(false);
1259+
when(() => argResults.wasParsed('workspace')).thenReturn(true);
1260+
when(() => argResults['workspace'] as bool?).thenReturn(false);
1261+
1262+
final command =
1263+
_TestCreateSubCommandWithWorkspace(
1264+
template: template,
1265+
logger: logger,
1266+
generatorFromBundle: null,
1267+
)
1268+
..argResultOverrides = argResults
1269+
..createConfig = const VeryGoodCreateConfig(workspace: true);
1270+
1271+
expect(command.workspace, isFalse);
1272+
});
1273+
1274+
test('falls back to false when neither workspace source is set', () {
1275+
final argResults = _MockArgResults();
1276+
when(() => argResults.wasParsed(any())).thenReturn(false);
1277+
when(() => argResults['workspace'] as bool?).thenReturn(null);
1278+
1279+
final command = _TestCreateSubCommandWithWorkspace(
1280+
template: template,
1281+
logger: logger,
1282+
generatorFromBundle: null,
1283+
)..argResultOverrides = argResults;
1284+
1285+
expect(command.workspace, isFalse);
1286+
});
12381287
});
12391288

12401289
group('run', () {

0 commit comments

Comments
 (0)