Unverified Commit 3e7c388e authored by Alex Li's avatar Alex Li Committed by GitHub

`flutter config --list` (#135401)

Resolves #81831.

The PR improves the `config` command in below ways:
- Does not print the settings in usages or other options.
- Adds the `--list` flag to print the full settings list.
- Separates usages for settings and analytics.
- Prints the restart tip when clearing features.
parent 3ad9998c
...@@ -15,6 +15,11 @@ import '../runner/flutter_command_runner.dart'; ...@@ -15,6 +15,11 @@ import '../runner/flutter_command_runner.dart';
class ConfigCommand extends FlutterCommand { class ConfigCommand extends FlutterCommand {
ConfigCommand({ bool verboseHelp = false }) { ConfigCommand({ bool verboseHelp = false }) {
argParser.addFlag(
'list',
help: 'List all settings and their current values.',
negatable: false,
);
argParser.addFlag('analytics', argParser.addFlag('analytics',
hide: !verboseHelp, hide: !verboseHelp,
help: 'Enable or disable reporting anonymously tool usage statistics and crash reports.\n' help: 'Enable or disable reporting anonymously tool usage statistics and crash reports.\n'
...@@ -73,37 +78,7 @@ class ConfigCommand extends FlutterCommand { ...@@ -73,37 +78,7 @@ class ConfigCommand extends FlutterCommand {
bool get shouldUpdateCache => false; bool get shouldUpdateCache => false;
@override @override
String get usageFooter { String get usageFooter => '\n$analyticsUsage';
// List all config settings. for feature flags, include whether they
// are available.
final Map<String, Feature> featuresByName = <String, Feature>{};
final String channel = globals.flutterVersion.channel;
for (final Feature feature in allFeatures) {
final String? configSetting = feature.configSetting;
if (configSetting != null) {
featuresByName[configSetting] = feature;
}
}
String values = globals.config.keys
.map<String>((String key) {
String configFooter = '';
if (featuresByName.containsKey(key)) {
final FeatureChannelSetting setting = featuresByName[key]!.getSettingForChannel(channel);
if (!setting.available) {
configFooter = '(Unavailable)';
}
}
return ' $key: ${globals.config.getValue(key)} $configFooter';
}).join('\n');
if (values.isEmpty) {
values = ' No settings have been configured.';
}
final bool analyticsEnabled = globals.flutterUsage.enabled &&
!globals.flutterUsage.suppressAnalytics;
return
'\nSettings:\n$values\n\n'
'Analytics reporting is currently ${analyticsEnabled ? 'enabled' : 'disabled'}.';
}
/// Return null to disable analytics recording of the `config` command. /// Return null to disable analytics recording of the `config` command.
@override @override
...@@ -121,6 +96,11 @@ class ConfigCommand extends FlutterCommand { ...@@ -121,6 +96,11 @@ class ConfigCommand extends FlutterCommand {
' flutter config --android-studio-dir "/opt/Android Studio"'); ' flutter config --android-studio-dir "/opt/Android Studio"');
} }
if (boolArg('list')) {
globals.printStatus(settingsText);
return FlutterCommandResult.success();
}
if (boolArg('machine')) { if (boolArg('machine')) {
await handleMachine(); await handleMachine();
return FlutterCommandResult.success(); return FlutterCommandResult.success();
...@@ -133,6 +113,7 @@ class ConfigCommand extends FlutterCommand { ...@@ -133,6 +113,7 @@ class ConfigCommand extends FlutterCommand {
globals.config.removeValue(configSetting); globals.config.removeValue(configSetting);
} }
} }
globals.printStatus(requireReloadTipText);
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
...@@ -195,7 +176,7 @@ class ConfigCommand extends FlutterCommand { ...@@ -195,7 +176,7 @@ class ConfigCommand extends FlutterCommand {
if (argResults == null || argResults!.arguments.isEmpty) { if (argResults == null || argResults!.arguments.isEmpty) {
globals.printStatus(usage); globals.printStatus(usage);
} else { } else {
globals.printStatus('\nYou may need to restart any open editors for them to read new settings.'); globals.printStatus('\n$requireReloadTipText');
} }
return FlutterCommandResult.success(); return FlutterCommandResult.success();
...@@ -234,4 +215,50 @@ class ConfigCommand extends FlutterCommand { ...@@ -234,4 +215,50 @@ class ConfigCommand extends FlutterCommand {
globals.printStatus('Setting "$keyName" value to "$keyValue".'); globals.printStatus('Setting "$keyName" value to "$keyValue".');
} }
} }
/// List all config settings. for feature flags, include whether they are available.
String get settingsText {
final Map<String, Feature> featuresByName = <String, Feature>{};
final String channel = globals.flutterVersion.channel;
for (final Feature feature in allFeatures) {
final String? configSetting = feature.configSetting;
if (configSetting != null) {
featuresByName[configSetting] = feature;
}
}
final Set<String> keys = <String>{
...allFeatures.map((Feature e) => e.configSetting).whereType<String>(),
...globals.config.keys,
};
final Iterable<String> settings = keys.map<String>((String key) {
Object? value = globals.config.getValue(key);
value ??= '(Not set)';
final StringBuffer buffer = StringBuffer(' $key: $value');
if (featuresByName.containsKey(key)) {
final FeatureChannelSetting setting = featuresByName[key]!.getSettingForChannel(channel);
if (!setting.available) {
buffer.write(' (Unavailable)');
}
}
return buffer.toString();
});
final StringBuffer buffer = StringBuffer();
buffer.writeln('All Settings:');
if (settings.isEmpty) {
buffer.writeln(' No configs have been configured.');
} else {
buffer.writeln(settings.join('\n'));
}
return buffer.toString();
}
/// List the status of the analytics reporting.
String get analyticsUsage {
final bool analyticsEnabled =
globals.flutterUsage.enabled && !globals.flutterUsage.suppressAnalytics;
return 'Analytics reporting is currently ${analyticsEnabled ? 'enabled' : 'disabled'}.';
}
/// Raising the reload tip for setting changes.
final String requireReloadTipText = 'You may need to restart any open editors for them to read new settings.';
} }
...@@ -12,6 +12,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/config.dart'; import 'package:flutter_tools/src/commands/config.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/version.dart'; import 'package:flutter_tools/src/version.dart';
...@@ -48,6 +49,23 @@ void main() { ...@@ -48,6 +49,23 @@ void main() {
} }
group('config', () { group('config', () {
testUsingContext('prints all settings with --list', () async {
final ConfigCommand configCommand = ConfigCommand();
final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand);
await commandRunner.run(<String>['config', '--list']);
expect(
testLogger.statusText,
'All Settings:\n'
'${allFeatures
.where((Feature e) => e.configSetting != null)
.map((Feature e) => ' ${e.configSetting}: (Not set)')
.join('\n')}'
'\n\n',
);
}, overrides: <Type, Generator>{
Usage: () => testUsage,
});
testUsingContext('throws error on excess arguments', () { testUsingContext('throws error on excess arguments', () {
final ConfigCommand configCommand = ConfigCommand(); final ConfigCommand configCommand = ConfigCommand();
final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand); final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand);
...@@ -196,6 +214,7 @@ void main() { ...@@ -196,6 +214,7 @@ void main() {
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'config', 'config',
'--list'
]); ]);
expect( expect(
...@@ -270,20 +289,21 @@ void main() { ...@@ -270,20 +289,21 @@ void main() {
Usage: () => testUsage, Usage: () => testUsage,
}); });
testUsingContext('analytics reported disabled when suppressed', () async { testUsingContext('analytics reported with help usages', () async {
final ConfigCommand configCommand = ConfigCommand(); final ConfigCommand configCommand = ConfigCommand();
final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand); createTestCommandRunner(configCommand);
testUsage.suppressAnalytics = true; testUsage.suppressAnalytics = true;
await commandRunner.run(<String>[
'config',
]);
expect( expect(
testLogger.statusText, configCommand.usage,
containsIgnoringWhitespace('Analytics reporting is currently disabled'), containsIgnoringWhitespace('Analytics reporting is currently disabled'),
); );
testUsage.suppressAnalytics = false;
expect(
configCommand.usage,
containsIgnoringWhitespace('Analytics reporting is currently enabled'),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Usage: () => testUsage, Usage: () => testUsage,
}); });
......
...@@ -71,11 +71,12 @@ void main() { ...@@ -71,11 +71,12 @@ void main() {
expect(result.stdout, contains('Shutdown hooks complete')); expect(result.stdout, contains('Shutdown hooks complete'));
}); });
testWithoutContext('flutter config contains all features', () async { testWithoutContext('flutter config --list contains all features', () async {
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
final ProcessResult result = await processManager.run(<String>[ final ProcessResult result = await processManager.run(<String>[
flutterBin, flutterBin,
'config', 'config',
'--list'
]); ]);
// contains all of the experiments in features.dart // contains all of the experiments in features.dart
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment