Unverified Commit 99b44599 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Allow disabling experimental commands, devices on stable branch (#30153)

parent 294d7ea0
...@@ -31,6 +31,9 @@ class BuildWebCommand extends BuildSubCommand { ...@@ -31,6 +31,9 @@ class BuildWebCommand extends BuildSubCommand {
@override @override
bool get hidden => true; bool get hidden => true;
@override
bool get isExperimental => true;
@override @override
final String description = '(EXPERIMENTAL) build a web application bundle.'; final String description = '(EXPERIMENTAL) build a web application bundle.';
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'base/platform.dart'; import 'base/platform.dart';
import 'version.dart';
// Only launch or display desktop embedding devices if // Only launch or display desktop embedding devices if
// `FLUTTER_DESKTOP_EMBEDDING` environment variable is set to true. // `FLUTTER_DESKTOP_EMBEDDING` environment variable is set to true.
bool get flutterDesktopEnabled { bool get flutterDesktopEnabled {
_flutterDesktopEnabled ??= platform.environment['FLUTTER_DESKTOP_EMBEDDING']?.toLowerCase() == 'true'; _flutterDesktopEnabled ??= platform.environment['FLUTTER_DESKTOP_EMBEDDING']?.toLowerCase() == 'true';
return _flutterDesktopEnabled; return _flutterDesktopEnabled && !FlutterVersion.instance.isStable;
} }
bool _flutterDesktopEnabled; bool _flutterDesktopEnabled;
...@@ -27,6 +27,7 @@ import '../doctor.dart'; ...@@ -27,6 +27,7 @@ import '../doctor.dart';
import '../globals.dart'; import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../usage.dart'; import '../usage.dart';
import '../version.dart';
import 'flutter_command_runner.dart'; import 'flutter_command_runner.dart';
export '../cache.dart' show DevelopmentArtifact; export '../cache.dart' show DevelopmentArtifact;
...@@ -464,6 +465,11 @@ abstract class FlutterCommand extends Command<void> { ...@@ -464,6 +465,11 @@ abstract class FlutterCommand extends Command<void> {
} }
} }
/// Whether this feature should not be usable on stable branches.
///
/// Defaults to false, meaning it is usable.
bool get isExperimental => false;
/// Additional usage values to be sent with the usage ping. /// Additional usage values to be sent with the usage ping.
Future<Map<String, String>> get usageValues async => const <String, String>{}; Future<Map<String, String>> get usageValues async => const <String, String>{};
...@@ -635,6 +641,12 @@ abstract class FlutterCommand extends Command<void> { ...@@ -635,6 +641,12 @@ abstract class FlutterCommand extends Command<void> {
@protected @protected
@mustCallSuper @mustCallSuper
Future<void> validateCommand() async { Future<void> validateCommand() async {
// If we're on a stable branch, then don't allow the usage of
// "experimental" features.
if (isExperimental && FlutterVersion.instance.isStable) {
throwToolExit('Experimental feature $name is not supported on stable branches');
}
if (_requiresPubspecYaml && !PackageMap.isUsingCustomPackagesPath) { if (_requiresPubspecYaml && !PackageMap.isUsingCustomPackagesPath) {
// Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path. // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path.
if (!fs.isFileSync('pubspec.yaml')) { if (!fs.isFileSync('pubspec.yaml')) {
......
...@@ -32,6 +32,11 @@ class FlutterVersion { ...@@ -32,6 +32,11 @@ class FlutterVersion {
return _repositoryUrl; return _repositoryUrl;
} }
/// Whether we are currently on the stable branch.
bool get isStable {
return getBranchName() == 'stable';
}
static const Set<String> officialChannels = <String>{ static const Set<String> officialChannels = <String>{
'master', 'master',
'dev', 'dev',
......
...@@ -13,6 +13,7 @@ import '../build_info.dart'; ...@@ -13,6 +13,7 @@ import '../build_info.dart';
import '../device.dart'; import '../device.dart';
import '../globals.dart'; import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../version.dart';
import '../web/compile.dart'; import '../web/compile.dart';
ChromeLauncher get chromeLauncher => context[ChromeLauncher]; ChromeLauncher get chromeLauncher => context[ChromeLauncher];
...@@ -21,7 +22,7 @@ ChromeLauncher get chromeLauncher => context[ChromeLauncher]; ...@@ -21,7 +22,7 @@ ChromeLauncher get chromeLauncher => context[ChromeLauncher];
/// environment variable is set to true. /// environment variable is set to true.
bool get flutterWebEnabled { bool get flutterWebEnabled {
_flutterWebEnabled = platform.environment['FLUTTER_WEB']?.toLowerCase() == 'true'; _flutterWebEnabled = platform.environment['FLUTTER_WEB']?.toLowerCase() == 'true';
return _flutterWebEnabled; return _flutterWebEnabled && !FlutterVersion.instance.isStable;
} }
bool _flutterWebEnabled; bool _flutterWebEnabled;
......
...@@ -7,6 +7,7 @@ import 'package:flutter_tools/src/base/time.dart'; ...@@ -7,6 +7,7 @@ import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/usage.dart'; import 'package:flutter_tools/src/usage.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import '../src/common.dart'; import '../src/common.dart';
...@@ -155,4 +156,43 @@ void main() { ...@@ -155,4 +156,43 @@ void main() {
}); });
}); });
group('Experimental commands', () {
final MockVersion stableVersion = MockVersion();
final MockVersion betaVersion = MockVersion();
final FakeCommand fakeCommand = FakeCommand();
when(stableVersion.isStable).thenReturn(true);
when(betaVersion.isStable).thenReturn(false);
testUsingContext('Can be disabled on stable branch', () async {
expect(() => fakeCommand.run(), throwsA(isA<ToolExit>()));
}, overrides: <Type, Generator>{
FlutterVersion: () => stableVersion,
});
testUsingContext('Works normally on regular branches', () async {
expect(fakeCommand.run(), completes);
}, overrides: <Type, Generator>{
FlutterVersion: () => betaVersion,
});
});
}
class FakeCommand extends FlutterCommand {
@override
String get description => null;
@override
String get name => 'fake';
@override
bool get isExperimental => true;
@override
Future<FlutterCommandResult> runCommand() async {
return null;
}
} }
class MockVersion extends Mock implements FlutterVersion {}
...@@ -304,7 +304,10 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter { ...@@ -304,7 +304,10 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter {
} }
} }
class MockFlutterVersion extends Mock implements FlutterVersion {} class MockFlutterVersion extends Mock implements FlutterVersion {
@override
bool get isStable => false;
}
class MockClock extends Mock implements SystemClock {} class MockClock extends Mock implements SystemClock {}
......
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