Guess the branch for the current stable | beta commit.

On release branches we don't know the branch name the commit is coming
from. In some cases knowing the source branch is required to reliably
use the correct versions of dependent repositories to use. An example of
this is engine builds checking out framework, framework checking out
plugins or adhoc tests checking out the correct versions of the code.

Bug: https://github.com/flutter/flutter/issues/94948
Bug: https://github.com/flutter/flutter/issues/94949
Bug: https://github.com/flutter/flutter/issues/91921
Change-Id: Ide053d33f330871515c04c9aa544a25c178e87bd
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/25660
Reviewed-by: Casey Hillers <chillers@google.com>
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
diff --git a/recipe_modules/repo_util/__init__.py b/recipe_modules/repo_util/__init__.py
index ecd1a9a..bbd16a6 100644
--- a/recipe_modules/repo_util/__init__.py
+++ b/recipe_modules/repo_util/__init__.py
@@ -13,4 +13,5 @@
     'recipe_engine/platform',
     'recipe_engine/properties',
     'recipe_engine/step',
+    'recipe_engine/raw_io',
 ]
diff --git a/recipe_modules/repo_util/api.py b/recipe_modules/repo_util/api.py
index 37c0662..33bb9c4 100644
--- a/recipe_modules/repo_util/api.py
+++ b/recipe_modules/repo_util/api.py
@@ -117,6 +117,37 @@
 
       return self.m.utils.retry(do_checkout, max_attempts=2)
 
+  def get_branch(self, checkout_path):
+    """Get git branch for beta and stable channels.
+
+    Post submit tests for release candidate branches pass the channel stable|beta in
+    the git_branch property. As the commits are being tested before they are publised
+    is not possible to use the channels to checkout the correct versions of dependencies
+    from other repositories, furthermore the channels are only applicable to
+    flutter/flutter repository. For tests with dependencies on other repositories we are
+    using the release candidate branch to check out the equivalent to the commit under test.
+    E.g. if the current commit comes from flutter@flutter-2.8-candiddate.16 then we checkout
+    plugins@flutter-2.8-candiddate.16.
+
+    To guess the branch name we get the current commit from the checkout and then we find
+    all the different branches the commit exist on, if there is a branch that starts with
+    flutter- then we assume that's the release candidate branch under test.
+    """
+    if self.m.properties.get('git_branch', '') in ['beta', 'stable']:
+      with self.m.context(cwd=checkout_path):
+        commit = self.m.git(
+                'rev-parse', 'HEAD',
+                stdout=self.m.raw_io.output_text()).stdout.strip()
+        branches = self.m.git(
+                'branch', '-a', '--contains', commit,
+                stdout=self.m.raw_io.output_text()).stdout.splitlines()
+        branches = [b.strip().replace('remotes/origin/', '') for b in branches]
+        return next(
+                iter(filter(lambda branch: branch.startswith('flutter-'), branches)),
+                self.m.properties.get('git_branch', '')
+        )
+    return self.m.properties.get('git_branch', '')
+
   def flutter_environment(self, checkout_path):
     """Returns env and env_prefixes of an flutter/dart command environment."""
     dart_bin = checkout_path.join('bin', 'cache', 'dart-sdk', 'bin')
@@ -148,7 +179,7 @@
             re.sub('refs\/pull\/|\/head', '', git_ref),
         'LUCI_BRANCH':
             self.m.properties.get('release_ref', '').replace('refs/heads/', ''),
-        'GIT_BRANCH': self.m.properties.get('git_branch', ''),
+        'GIT_BRANCH': self.get_branch(checkout_path),
         'OS':
             'linux' if self.m.platform.name == 'linux' else
             ('darwin' if self.m.platform.name == 'mac' else 'win'),
diff --git a/recipe_modules/repo_util/examples/full.expected/basic.json b/recipe_modules/repo_util/examples/full.expected/basic.json
index cb0e543..255199b 100644
--- a/recipe_modules/repo_util/examples/full.expected/basic.json
+++ b/recipe_modules/repo_util/examples/full.expected/basic.json
@@ -1,5 +1,27 @@
 [
   {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "git rev-parse"
+  },
+  {
+    "cmd": [
+      "git",
+      "branch",
+      "-a",
+      "--contains",
+      "abchash"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "git branch"
+  },
+  {
     "cmd": [],
     "name": "Checkout flutter/flutter"
   },
@@ -444,6 +466,28 @@
     ]
   },
   {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "git rev-parse (2)"
+  },
+  {
+    "cmd": [
+      "git",
+      "branch",
+      "-a",
+      "--contains",
+      ""
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "git branch (2)"
+  },
+  {
     "cmd": [],
     "name": "Checkout source code"
   },
diff --git a/recipe_modules/repo_util/examples/full.py b/recipe_modules/repo_util/examples/full.py
index 10d35fa..cead6b6 100644
--- a/recipe_modules/repo_util/examples/full.py
+++ b/recipe_modules/repo_util/examples/full.py
@@ -9,11 +9,13 @@
     'recipe_engine/context',
     'recipe_engine/path',
     'recipe_engine/properties',
+    'recipe_engine/raw_io',
 ]
 
 
 def RunSteps(api):
   flutter_checkout_path = api.path['start_dir'].join('flutter')
+  api.repo_util.get_branch(flutter_checkout_path)
   api.repo_util.checkout('flutter', flutter_checkout_path, ref='refs/heads/master')
   api.repo_util.checkout('engine', api.path['start_dir'].join('engine'), ref='refs/heads/master')
   api.repo_util.checkout('cocoon', api.path['start_dir'].join('cocoon'), ref='refs/heads/master')
@@ -26,7 +28,13 @@
 
 
 def GenTests(api):
-  yield api.test('basic') + api.repo_util.flutter_environment_data()
+  yield (
+      api.test(
+          'basic',
+          api.properties(git_branch='beta'),
+          api.repo_util.flutter_environment_data(),
+          api.step_data('git rev-parse', stdout=api.raw_io.output_text('abchash')))
+  )
   yield api.test('failed_flutter_environment')
   yield (
       api.test(