Unverified Commit 0021a08c authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Always activate DevTools if it's not installed (#81242)

parent 8071576c
...@@ -2974,7 +2974,7 @@ Iterable<DiagnosticsNode> _describeRelevantUserCode( ...@@ -2974,7 +2974,7 @@ Iterable<DiagnosticsNode> _describeRelevantUserCode(
// errors. See https://github.com/flutter/flutter/issues/74918. // errors. See https://github.com/flutter/flutter/issues/74918.
if (isOverflowError()) { if (isOverflowError()) {
final String? devToolsInspectorUri = final String? devToolsInspectorUri =
WidgetInspectorService.instance._devToolsInspectorUriForElement(target); WidgetInspectorService.instance._devToolsInspectorUriForElement(target);
if (devToolsInspectorUri != null) { if (devToolsInspectorUri != null) {
devToolsDiagnostic = DevToolsDeepLinkProperty( devToolsDiagnostic = DevToolsDeepLinkProperty(
'To inspect this widget in Flutter DevTools, visit: $devToolsInspectorUri', 'To inspect this widget in Flutter DevTools, visit: $devToolsInspectorUri',
......
...@@ -90,11 +90,14 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -90,11 +90,14 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
// couple scripts. See https://github.com/flutter/devtools/issues/2420. // couple scripts. See https://github.com/flutter/devtools/issues/2420.
return; return;
} else { } else {
final bool didActivateDevTools = await _activateDevTools(); bool devToolsActive = await _checkForActiveDevTools();
final bool devToolsActive = await _checkForActiveDevTools(); await _activateDevTools(throttleUpdates: devToolsActive);
if (!didActivateDevTools && !devToolsActive) { if (!devToolsActive) {
// At this point, we failed to activate the DevTools package and the devToolsActive = await _checkForActiveDevTools();
// package is not already active. }
if (!devToolsActive) {
// We don't have devtools installed and installing it failed;
// _activateDevTools will have reported the error already.
return; return;
} }
} }
...@@ -135,51 +138,51 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -135,51 +138,51 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
} }
} }
static final RegExp _devToolsInstalledPattern = RegExp(r'^devtools ', multiLine: true);
/// Check if the DevTools package is already active by running "pub global list".
Future<bool> _checkForActiveDevTools() async { Future<bool> _checkForActiveDevTools() async {
// We are offline, and cannot activate DevTools, so check if the DevTools final io.ProcessResult _pubGlobalListProcess = await _processManager.run(
// package is already active. <String>[ _pubExecutable, 'global', 'list' ],
final io.ProcessResult _pubGlobalListProcess = await _processManager.run(<String>[ );
_pubExecutable, return _pubGlobalListProcess.stdout.toString().contains(_devToolsInstalledPattern);
'global',
'list',
]);
if (_pubGlobalListProcess.stdout.toString().contains('devtools ')) {
return true;
}
return false;
} }
/// Helper method to activate the DevTools pub package. /// Helper method to activate the DevTools pub package.
/// ///
/// Returns a bool indicating whether or not the package was successfully /// If throttleUpdates is true, then this is a no-op if it was run in
/// activated from pub. /// the last twelve hours. It should be set to true if devtools is known
Future<bool> _activateDevTools() async { /// to already be installed.
final DateTime now = DateTime.now(); ///
// Only attempt to activate DevTools twice a day. /// Return value indicates if DevTools was installed or updated.
final bool shouldActivate = Future<bool> _activateDevTools({@required bool throttleUpdates}) async {
_persistentToolState.lastDevToolsActivationTime == null || assert(throttleUpdates != null);
now.difference(_persistentToolState.lastDevToolsActivationTime).inHours >= 12; const Duration _throttleDuration = Duration(hours: 12);
if (!shouldActivate) { if (throttleUpdates) {
return false; if (_persistentToolState.lastDevToolsActivationTime != null &&
DateTime.now().difference(_persistentToolState.lastDevToolsActivationTime) < _throttleDuration) {
_logger.printTrace('DevTools activation throttled until ${_persistentToolState.lastDevToolsActivationTime.add(_throttleDuration).toLocal()}.');
return false; // Throttled.
}
} }
final Status status = _logger.startProgress( final Status status = _logger.startProgress('Activating Dart DevTools...');
'Activating Dart DevTools...',
);
try { try {
final io.ProcessResult _devToolsActivateProcess = await _processManager final io.ProcessResult _devToolsActivateProcess = await _processManager
.run(<String>[ .run(<String>[
_pubExecutable, _pubExecutable,
'global', 'global',
'activate', 'activate',
'devtools' 'devtools',
]); ]);
if (_devToolsActivateProcess.exitCode != 0) { if (_devToolsActivateProcess.exitCode != 0) {
_logger.printError('Error running `pub global activate ' _logger.printError(
'devtools`:\n${_devToolsActivateProcess.stderr}'); 'Error running `pub global activate devtools`:\n'
return false; '${_devToolsActivateProcess.stderr}'
);
return false; // Failed to activate.
} }
_persistentToolState.lastDevToolsActivation = DateTime.now(); _persistentToolState.lastDevToolsActivation = DateTime.now();
return true; return true; // Activation succeeded!
} on Exception catch (e, _) { } on Exception catch (e, _) {
_logger.printError('Error running `pub global activate devtools`: $e'); _logger.printError('Error running `pub global activate devtools`: $e');
return false; return false;
......
...@@ -25,6 +25,12 @@ abstract class ResidentDevtoolsHandler { ...@@ -25,6 +25,12 @@ abstract class ResidentDevtoolsHandler {
/// The current devtools server, or null if one is not running. /// The current devtools server, or null if one is not running.
DevToolsServerAddress get activeDevToolsServer; DevToolsServerAddress get activeDevToolsServer;
/// Whether it's ok to announce the [activeDevToolsServer].
///
/// This should only return true once all the devices have been notified
/// of the DevTools.
bool get readyToAnnounce;
Future<void> hotRestart(List<FlutterDevice> flutterDevices); Future<void> hotRestart(List<FlutterDevice> flutterDevices);
Future<void> serveAndAnnounceDevTools({Uri devToolsServerAddress, List<FlutterDevice> flutterDevices}); Future<void> serveAndAnnounceDevTools({Uri devToolsServerAddress, List<FlutterDevice> flutterDevices});
...@@ -44,6 +50,10 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -44,6 +50,10 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
@override @override
DevToolsServerAddress get activeDevToolsServer => _devToolsLauncher?.activeDevToolsServer; DevToolsServerAddress get activeDevToolsServer => _devToolsLauncher?.activeDevToolsServer;
@override
bool get readyToAnnounce => _readyToAnnounce;
bool _readyToAnnounce = false;
// This must be guaranteed not to return a Future that fails. // This must be guaranteed not to return a Future that fails.
@override @override
Future<void> serveAndAnnounceDevTools({ Future<void> serveAndAnnounceDevTools({
...@@ -60,18 +70,15 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -60,18 +70,15 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
await _devToolsLauncher.serve(); await _devToolsLauncher.serve();
} }
await _devToolsLauncher.ready; await _devToolsLauncher.ready;
final List<FlutterDevice> devicesWithExtension = await _devicesWithExtensions(flutterDevices);
await _maybeCallDevToolsUriServiceExtension(devicesWithExtension);
await _callConnectedVmServiceUriExtension(devicesWithExtension);
_readyToAnnounce = true;
if (_residentRunner.reportedDebuggers) { if (_residentRunner.reportedDebuggers) {
// Since the DevTools only just became available, we haven't had a chance to // Since the DevTools only just became available, we haven't had a chance to
// report their URLs yet. Do so now. // report their URLs yet. Do so now.
_residentRunner.printDebuggerList(includeObservatory: false); _residentRunner.printDebuggerList(includeObservatory: false);
} }
final List<FlutterDevice> devicesWithExtension = await _devicesWithExtensions(flutterDevices);
await _maybeCallDevToolsUriServiceExtension(
devicesWithExtension,
);
await _callConnectedVmServiceUriExtension(
devicesWithExtension,
);
} }
Future<void> _maybeCallDevToolsUriServiceExtension( Future<void> _maybeCallDevToolsUriServiceExtension(
...@@ -213,6 +220,9 @@ class NoOpDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -213,6 +220,9 @@ class NoOpDevtoolsHandler implements ResidentDevtoolsHandler {
@override @override
DevToolsServerAddress get activeDevToolsServer => null; DevToolsServerAddress get activeDevToolsServer => null;
@override
bool get readyToAnnounce => false;
@override @override
Future<void> hotRestart(List<FlutterDevice> flutterDevices) async { Future<void> hotRestart(List<FlutterDevice> flutterDevices) async {
return; return;
......
...@@ -1423,9 +1423,10 @@ abstract class ResidentRunner extends ResidentHandlers { ...@@ -1423,9 +1423,10 @@ abstract class ResidentRunner extends ResidentHandlers {
void printDebuggerList({ bool includeObservatory = true, bool includeDevtools = true }) { void printDebuggerList({ bool includeObservatory = true, bool includeDevtools = true }) {
final DevToolsServerAddress devToolsServerAddress = residentDevtoolsHandler.activeDevToolsServer; final DevToolsServerAddress devToolsServerAddress = residentDevtoolsHandler.activeDevToolsServer;
if (devToolsServerAddress == null) { if (!residentDevtoolsHandler.readyToAnnounce) {
includeDevtools = false; includeDevtools = false;
} }
assert(!includeDevtools || devToolsServerAddress != null);
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
if (device.vmService == null) { if (device.vmService == null) {
continue; continue;
......
...@@ -107,18 +107,18 @@ void main() { ...@@ -107,18 +107,18 @@ void main() {
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'activate', 'list',
'devtools',
], ],
stdout: 'Activated DevTools 0.9.5', stdout: 'devtools 0.9.6',
), ),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'list', 'activate',
'devtools',
], ],
stdout: 'devtools 0.9.6', stdout: 'Activated DevTools 0.9.6',
), ),
FakeCommand( FakeCommand(
command: const <String>[ command: const <String>[
...@@ -148,6 +148,14 @@ void main() { ...@@ -148,6 +148,14 @@ void main() {
persistentToolState: persistentToolState, persistentToolState: persistentToolState,
httpClient: FakeHttpClient.any(), httpClient: FakeHttpClient.any(),
processManager: FakeProcessManager.list(<FakeCommand>[ processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'pub',
'global',
'list',
],
stdout: '',
),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
'pub', 'pub',
...@@ -163,7 +171,7 @@ void main() { ...@@ -163,7 +171,7 @@ void main() {
'global', 'global',
'list', 'list',
], ],
stdout: 'devtools 0.9.6', stdout: 'devtools 0.9.5',
), ),
FakeCommand( FakeCommand(
command: const <String>[ command: const <String>[
...@@ -197,18 +205,18 @@ void main() { ...@@ -197,18 +205,18 @@ void main() {
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'activate', 'list',
'devtools',
], ],
stdout: 'Activated DevTools 0.9.5', stdout: 'devtools 0.9.5',
), ),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'list', 'activate',
'devtools',
], ],
stdout: 'devtools 0.9.6', stdout: 'Activated DevTools 0.9.5',
), ),
FakeCommand( FakeCommand(
command: const <String>[ command: const <String>[
...@@ -279,19 +287,19 @@ void main() { ...@@ -279,19 +287,19 @@ void main() {
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'activate', 'list',
'devtools',
], ],
stderr: 'Error - could not activate devtools', stdout: 'devtools 0.9.6',
exitCode: 1,
), ),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'list', 'activate',
'devtools',
], ],
stdout: 'devtools 0.9.6', stderr: 'Error - could not activate devtools',
exitCode: 1,
), ),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
...@@ -324,18 +332,18 @@ void main() { ...@@ -324,18 +332,18 @@ void main() {
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'activate', 'list',
'devtools',
], ],
stdout: 'Activated DevTools 0.9.5', stdout: 'devtools 0.9.5',
), ),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'list', 'activate',
'devtools',
], ],
stdout: 'devtools 0.9.6', stdout: 'Activated DevTools 0.9.6',
), ),
const FakeCommand( const FakeCommand(
command: <String>[ command: <String>[
......
...@@ -514,7 +514,7 @@ void main() { ...@@ -514,7 +514,7 @@ void main() {
} finally { } finally {
tryToDelete(fileSystem.directory(tempDirectory)); tryToDelete(fileSystem.directory(tempDirectory));
} }
}, skip: 'DevTools does not reliably launch on bots currently.'); // TODO(ianh): fix and re-enable test. });
testWithoutContext('flutter run help output', () async { testWithoutContext('flutter run help output', () async {
// This test enables all logging so that it checks the exact text of starting up an application. // This test enables all logging so that it checks the exact text of starting up an application.
......
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