dt: allow DEVTOOLS_TOOL_FLUTTER_FROM_PATH to be used in CI scripts (#9999)
diff --git a/tool/bin/dt b/tool/bin/dt
index 9955ea2..9893b7b 100755
--- a/tool/bin/dt
+++ b/tool/bin/dt
@@ -2,9 +2,19 @@
 
 SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
 
-if [ ! -z "$DEVTOOLS_TOOL_FLUTTER_FROM_PATH" ]
+USE_PATH=false
+if [ ! -z "$DEVTOOLS_TOOL_FLUTTER_FROM_PATH" ]; then
+  USE_PATH=true
+fi
+for arg in "$@"; do
+  if [ "$arg" = "-p" ] || [ "$arg" = "--flutter-from-path" ]; then
+    USE_PATH=true
+  fi
+done
+
+if [ "$USE_PATH" = true ]
 then
-  echo Running dt using Dart/Flutter from PATH because DEVTOOLS_TOOL_FLUTTER_FROM_PATH is set
+  echo Running dt using Dart/Flutter from PATH
   dart run "$SCRIPT_DIR/dt.dart" "$@"
 else
   if [ ! -d $SCRIPT_DIR/../flutter-sdk ]
diff --git a/tool/bin/dt.bat b/tool/bin/dt.bat
index 10b73d3..a321c4a 100755
--- a/tool/bin/dt.bat
+++ b/tool/bin/dt.bat
@@ -3,8 +3,15 @@
 REM found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
 @echo off
 
-IF DEFINED DEVTOOLS_TOOL_FLUTTER_FROM_PATH (
-	echo Running dt using Dart/Flutter from PATH because DEVTOOLS_TOOL_FLUTTER_FROM_PATH is set
+set USE_PATH=
+IF DEFINED DEVTOOLS_TOOL_FLUTTER_FROM_PATH set USE_PATH=1
+for %%a in (%*) do (
+	if "%%a"=="-p" set USE_PATH=1
+	if "%%a"=="--flutter-from-path" set USE_PATH=1
+)
+
+IF DEFINED USE_PATH (
+	echo Running dt using Dart/Flutter from PATH
 	dart run %~dp0/dt.dart %*
 ) ELSE (
 
diff --git a/tool/ci/setup.sh b/tool/ci/setup.sh
index 57df3a5..d02446c 100755
--- a/tool/ci/setup.sh
+++ b/tool/ci/setup.sh
@@ -21,17 +21,34 @@
 }
 export -f flutter
 
-# Make sure Flutter sdk has been provided
-if [ ! -d "./tool/flutter-sdk" ]; then
-    echo "Expected ./tool/flutter-sdk to exist"
-    exit 1;
+# Determine the Flutter SDK to use:
+# * If `DEVTOOLS_TOOL_FLUTTER_FROM_PATH` is set, then discover from `PATH`.
+# * If `./tool/flutter-sdk` (a directory) exists, then use that.
+if [ -n "$DEVTOOLS_TOOL_FLUTTER_FROM_PATH" ]; then
+    if command -v flutter &> /dev/null; then
+        FLUTTER_EXE="$(command -v flutter)"
+    elif command -v flutter.bat &> /dev/null; then
+        FLUTTER_EXE="$(command -v flutter.bat)"
+    else
+        echo "DEVTOOLS_TOOL_FLUTTER_FROM_PATH is set, but flutter was not found on PATH"
+        exit 1
+    fi
+    FLUTTER_BIN="$(cd "$(dirname "$FLUTTER_EXE")" && pwd -P)"
+    FLUTTER_DIR="$(cd "$FLUTTER_BIN/.." && pwd -P)"
+    echo "Using Flutter from PATH at: $FLUTTER_DIR"
+    export DEVTOOLS_TOOL_FLUTTER_FROM_PATH=true
+elif [ -d "./tool/flutter-sdk" ]; then
+    FLUTTER_DIR="$(pwd)/tool/flutter-sdk"
+else
+    echo "Expected ./tool/flutter-sdk to exist, or DEVTOOLS_TOOL_FLUTTER_FROM_PATH to be set"
+    exit 1
 fi
 
-# Look in the dart bin dir first, then the flutter one, then the one for the
-# devtools repo. We don't use the dart script from flutter/bin as that script
-# can and does print 'Waiting for another flutter command...' at inopportune
-# times.
-export PATH=`pwd`/tool/flutter-sdk/bin/cache/dart-sdk/bin:`pwd`/tool/flutter-sdk/bin:`pwd`/bin:$PATH
+# Look in the dart bin/ directory first, then the flutter one, then the one for
+# the devtools repo. We don't use the dart script from 'flutter/bin' as that
+# script can and does print 'Waiting for another flutter command...' at
+# inopportune times.
+export PATH="$FLUTTER_DIR/bin/cache/dart-sdk/bin:$FLUTTER_DIR/bin:`pwd`/bin:$PATH"
 
 # Look up the latest flutter candidate (this is the latest flutter version in g3)
 # TODO(https://github.com/flutter/devtools/issues/4591): re-write this script as a
@@ -40,7 +57,7 @@
 flutter config --no-analytics
 flutter doctor
 
-# We should be using dart from ../flutter-sdk/bin/cache/dart-sdk/dart.
+# We should be using dart from the Flutter SDK's cache/dart-sdk/bin/ directory.
 echo "which flutter: " `which flutter`
 echo "which dart: " `which dart`
 
diff --git a/tool/lib/devtools_command_runner.dart b/tool/lib/devtools_command_runner.dart
index 7d59544..727da3a 100644
--- a/tool/lib/devtools_command_runner.dart
+++ b/tool/lib/devtools_command_runner.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
 
+import 'dart:io';
+
 import 'package:args/args.dart';
 import 'package:args/command_runner.dart';
 import 'package:devtools_tool/commands/build.dart';
@@ -28,6 +30,8 @@
 
 const _flutterFromPathFlag = 'flutter-from-path';
 
+const _flutterFromPathEnvVar = 'DEVTOOLS_TOOL_FLUTTER_FROM_PATH';
+
 const _flutterSdkPathFlag = 'flutter-sdk-path';
 
 class DevToolsCommandRunner extends CommandRunner {
@@ -62,7 +66,9 @@
             'Use the Flutter SDK on PATH for any `flutter`, `dart` and '
             '`dt` commands spawned by this process, instead of the '
             'Flutter SDK from tool/flutter-sdk which is used by default. '
-            'This is incompatible with the `$_flutterSdkPathFlag` flag.',
+            'This is incompatible with the `$_flutterSdkPathFlag` flag. '
+            'Can also be enabled via the `$_flutterFromPathEnvVar` '
+            'environment variable.',
       )
       ..addOption(
         _flutterSdkPathFlag,
@@ -76,6 +82,8 @@
 
   @override
   Future<void> runCommand(ArgResults topLevelResults) {
+    final flutterFromPathEnv =
+        Platform.environment[_flutterFromPathEnvVar]?.isNotEmpty == true;
     if (topLevelResults.flag(_flutterFromPathFlag) &&
         topLevelResults.wasParsed(_flutterSdkPathFlag)) {
       throw ArgParserException(
@@ -84,7 +92,8 @@
     }
     if (topLevelResults.wasParsed(_flutterSdkPathFlag)) {
       FlutterSdk.useFromPath(topLevelResults.option(_flutterSdkPathFlag)!);
-    } else if (topLevelResults.flag(_flutterFromPathFlag)) {
+    } else if (topLevelResults.flag(_flutterFromPathFlag) ||
+        flutterFromPathEnv) {
       FlutterSdk.useFromPathEnvironmentVariable();
     } else {
       FlutterSdk.useFromCurrentVm();
diff --git a/tool/test/model_test.dart b/tool/test/model_test.dart
index 5279086..85682c0 100644
--- a/tool/test/model_test.dart
+++ b/tool/test/model_test.dart
@@ -4,11 +4,29 @@
 
 import 'dart:io';
 
+import 'package:devtools_tool/devtools_command_runner.dart';
 import 'package:devtools_tool/model.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 
 void main() {
+  group('DevToolsCommandRunner', () {
+    test('parses --flutter-from-path flag', () {
+      final runner = DevToolsCommandRunner();
+      final results = runner.argParser.parse(['-p']);
+      expect(results['flutter-from-path'], isTrue);
+    });
+
+    test('parses --flutter-sdk-path option', () {
+      final runner = DevToolsCommandRunner();
+      final results = runner.argParser.parse([
+        '--flutter-sdk-path',
+        '/custom/path',
+      ]);
+      expect(results['flutter-sdk-path'], equals('/custom/path'));
+    });
+  });
+
   group('FlutterSdk.findFromPath', () {
     late Directory tempDir;