[flutter_releases] Fix dartdocs branch name on 2.2.x (#86723)

* teach dartdoc.dart about LUCI_BRANCH env var (#86592)
diff --git a/dev/tools/dartdoc.dart b/dev/tools/dartdoc.dart
index ca50839..3f37d8c 100644
--- a/dev/tools/dartdoc.dart
+++ b/dev/tools/dartdoc.dart
@@ -7,7 +7,9 @@
 
 import 'package:args/args.dart';
 import 'package:intl/intl.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
+import 'package:platform/platform.dart';
 import 'package:process/process.dart';
 
 import 'dartdoc_checker.dart';
@@ -253,8 +255,21 @@
 
 final RegExp gitBranchRegexp = RegExp(r'^## (.*)');
 
-String getBranchName() {
-  final ProcessResult gitResult = Process.runSync('git', <String>['status', '-b', '--porcelain']);
+/// Get the name of the release branch.
+///
+/// On LUCI builds, the git HEAD is detached, so first check for the env
+/// variable "LUCI_BRANCH"; if it is not set, fall back to calling git.
+String getBranchName({
+  @visibleForTesting
+  Platform platform = const LocalPlatform(),
+  @visibleForTesting
+  ProcessManager processManager = const LocalProcessManager(),
+}) {
+  final String luciBranch = platform.environment['LUCI_BRANCH'];
+  if (luciBranch != null && luciBranch.trim().isNotEmpty) {
+    return luciBranch.trim();
+  }
+  final ProcessResult gitResult = processManager.runSync(<String>['git', 'status', '-b', '--porcelain']);
   if (gitResult.exitCode != 0)
     throw 'git status exit with non-zero exit code: ${gitResult.exitCode}';
   final Match gitBranchMatch = gitBranchRegexp.firstMatch(
diff --git a/dev/tools/test/dartdoc_test.dart b/dev/tools/test/dartdoc_test.dart
new file mode 100644
index 0000000..af651f3
--- /dev/null
+++ b/dev/tools/test/dartdoc_test.dart
@@ -0,0 +1,81 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
+import 'package:test/test.dart';
+
+import '../../../packages/flutter_tools/test/src/fake_process_manager.dart';
+import '../dartdoc.dart' show getBranchName;
+
+void main() {
+  const String branchName = 'stable';
+  test('getBranchName does not call git if env LUCI_BRANCH provided', () {
+    final Platform platform = FakePlatform(
+      environment: <String, String>{
+        'LUCI_BRANCH': branchName,
+      },
+    );
+
+    final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
+
+    expect(
+      getBranchName(
+        platform: platform,
+        processManager: processManager,
+      ),
+      branchName,
+    );
+  });
+
+  test('getBranchName calls git if env LUCI_BRANCH not provided', () {
+    final Platform platform = FakePlatform(
+      environment: <String, String>{},
+    );
+
+    final ProcessManager processManager = FakeProcessManager.list(
+      <FakeCommand>[
+        const FakeCommand(
+          command: <String>['git', 'status', '-b', '--porcelain'],
+          stdout: '## $branchName',
+        ),
+      ],
+    );
+
+    expect(
+      getBranchName(
+        platform: platform,
+        processManager: processManager,
+      ),
+      branchName,
+    );
+    expect(processManager, hasNoRemainingExpectations);
+  });
+
+  test('getBranchName calls git if env LUCI_BRANCH is empty', () {
+    final Platform platform = FakePlatform(
+      environment: <String, String>{
+        'LUCI_BRANCH': '',
+      },
+    );
+
+    final ProcessManager processManager = FakeProcessManager.list(
+      <FakeCommand>[
+        const FakeCommand(
+          command: <String>['git', 'status', '-b', '--porcelain'],
+          stdout: '## $branchName',
+        ),
+      ],
+    );
+
+    expect(
+      getBranchName(
+        platform: platform,
+        processManager: processManager,
+      ),
+      branchName,
+    );
+    expect(processManager, hasNoRemainingExpectations);
+  });
+}