Allow flutter SDK binary to be a symlink (#9924)

* Fix SDK path resolution when Flutter on PATH is a symlink

When running `dt update-flutter-sdk --update-on-path`, `FlutterSdk.findFromPathEnvironmentVariable` failed if `flutter` on PATH was a symlink (e.g. `/usr/local/bin/flutter`). It stripped `bin/flutter` from the unresolved symlink path, resulting in `/usr/local` being treated as the SDK root directory.

This change:
- Updates `FlutterSdk.findFromPath` to resolve symbolic links before determining the SDK root directory.
- Updates `UpdateFlutterSdkCommand` to use the remote returned by `findRemote` instead of hardcoding 'upstream'.
- Adds unit tests in `tool/test/model_test.dart` for `FlutterSdk.findFromPath`.

* Revert change to "findRemote" behavior

* Revert change to "findRemote" behavior

* Simplify symlink resolution using try-catch fallback in FlutterSdk.findFromPath

* Add release note for dt sync symlink fix

* format

* Revert changes to release notes

* extract repeated calls to path.dirname

* Avoid explicit variable type declaration.

* Remove ignore for DCM
diff --git a/tool/lib/model.dart b/tool/lib/model.dart
index 2032b4c..12df455 100644
--- a/tool/lib/model.dart
+++ b/tool/lib/model.dart
@@ -242,8 +242,20 @@
   }
 
   static FlutterSdk findFromPath(String sdkPath) {
-    if (path.basename(path.dirname(sdkPath)) == 'bin') {
-      return FlutterSdk._(path.dirname(path.dirname(sdkPath)));
+    var resolvedPath = sdkPath;
+    try {
+      resolvedPath = File(sdkPath).resolveSymbolicLinksSync();
+    } catch (_) {
+      // Fallback to the unresolved path if resolution fails.
+    }
+
+    final resolvedPathParent = path.dirname(resolvedPath);
+    if (path.basename(resolvedPathParent) == 'bin') {
+      return FlutterSdk._(path.dirname(resolvedPathParent));
+    } else if (path.basename(resolvedPath) == 'bin') {
+      return FlutterSdk._(resolvedPathParent);
+    } else if (Directory(path.join(resolvedPath, 'bin')).existsSync()) {
+      return FlutterSdk._(resolvedPath);
     }
 
     throw Exception('Unable to locate the Flutter SDK at "$sdkPath"');
@@ -258,10 +270,7 @@
     final result = Process.runSync(whichCommand, ['flutter']);
     if (result.exitCode == 0) {
       final sdkPath = result.stdout.toString().split('\n').first.trim();
-      // 'flutter/bin'
-      if (path.basename(path.dirname(sdkPath)) == 'bin') {
-        return FlutterSdk._(path.dirname(path.dirname(sdkPath)));
-      }
+      return findFromPath(sdkPath);
     }
 
     throw Exception('Unable to locate the Flutter SDK on PATH');
diff --git a/tool/test/model_test.dart b/tool/test/model_test.dart
new file mode 100644
index 0000000..5279086
--- /dev/null
+++ b/tool/test/model_test.dart
@@ -0,0 +1,61 @@
+// Copyright 2026 The Flutter Authors
+// 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:devtools_tool/model.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+
+void main() {
+  group('FlutterSdk.findFromPath', () {
+    late Directory tempDir;
+
+    setUp(() {
+      tempDir = Directory.systemTemp.createTempSync('flutter_sdk_test_');
+    });
+
+    tearDown(() {
+      tempDir.deleteSync(recursive: true);
+    });
+
+    test('finds SDK root from bin/flutter executable path', () {
+      final binDir = Directory(path.join(tempDir.path, 'bin'))..createSync();
+      final flutterExe = File(path.join(binDir.path, 'flutter'))..createSync();
+
+      final sdk = FlutterSdk.findFromPath(flutterExe.path);
+      expect(sdk.sdkPath, equals(tempDir.resolveSymbolicLinksSync()));
+    });
+
+    test('resolves symbolic links to find actual SDK root', () {
+      final sdkDir = Directory(path.join(tempDir.path, 'real_sdk'))
+        ..createSync();
+      final binDir = Directory(path.join(sdkDir.path, 'bin'))..createSync();
+      final flutterExe = File(path.join(binDir.path, 'flutter'))..createSync();
+
+      final linkDir = Directory(path.join(tempDir.path, 'symlink_bin'))
+        ..createSync();
+      final symlink = Link(path.join(linkDir.path, 'flutter'));
+      symlink.createSync(flutterExe.path);
+
+      final sdk = FlutterSdk.findFromPath(symlink.path);
+      expect(sdk.sdkPath, equals(sdkDir.resolveSymbolicLinksSync()));
+    });
+
+    test('finds SDK root when given SDK directory path directly', () {
+      final binDir = Directory(path.join(tempDir.path, 'bin'))..createSync();
+      File(path.join(binDir.path, 'flutter')).createSync();
+
+      final sdk = FlutterSdk.findFromPath(tempDir.path);
+      expect(sdk.sdkPath, equals(tempDir.resolveSymbolicLinksSync()));
+    });
+
+    test('throws Exception when unable to locate Flutter SDK', () {
+      expect(
+        () => FlutterSdk.findFromPath('/non/existent/path/to/flutter'),
+        throwsA(isA<Exception>()),
+      );
+    });
+  });
+}