Ensure LUCI branch is set on documentation deployment.

This environment variable is required to set footer of the documentation
correctly.

Bug: https://github.com/flutter/flutter/issues/117157
Change-Id: I24aa3e0984155695273a3e78c058955fa404dafe
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/37505
Reviewed-by: Drew Roen <drewroen@google.com>
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
(cherry picked from commit ee17af0c91fd2e3710d78c150768c4ac7010dd7f)
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/37580
Reviewed-by: Xilai Zhang <xilaizhang@google.com>
diff --git a/recipe_modules/adhoc_validation/__init__.py b/recipe_modules/adhoc_validation/__init__.py
index c99e158..bf16104 100644
--- a/recipe_modules/adhoc_validation/__init__.py
+++ b/recipe_modules/adhoc_validation/__init__.py
@@ -1,6 +1,6 @@
-PYTHON_VERSION_COMPATIBILITY = 'PY3'
-
 DEPS = [
+    'flutter/archives',
+    'flutter/flutter_bcid',
     'flutter/bucket_util',
     'flutter/firebase',
     'flutter/flutter_deps',
@@ -8,6 +8,7 @@
     'flutter/osx_sdk',
     'flutter/repo_util',
     'flutter/test_utils',
+    'recipe_engine/buildbucket',
     'recipe_engine/context',
     'recipe_engine/platform',
     'recipe_engine/properties',
diff --git a/recipe_modules/adhoc_validation/api.py b/recipe_modules/adhoc_validation/api.py
index 275a09c..7ca0fd9 100644
--- a/recipe_modules/adhoc_validation/api.py
+++ b/recipe_modules/adhoc_validation/api.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 from recipe_engine import recipe_api
+from RECIPE_MODULES.flutter.flutter_bcid.api import BcidStage
 
 
 class AddhocValidationApi(recipe_api.RecipeApi):
@@ -32,7 +33,7 @@
       secrets(dict): The key is the name of the secret and value is the path to kms.
     """
     if validation not in self.available_validations():
-      msg = validation + ' is not listed in available_validations.'
+      msg = str(validation) + ' is not listed in available_validations.'
       raise AssertionError(msg)
     secrets = secrets or {}
     with self.m.step.nest(name):
@@ -56,7 +57,12 @@
               env, env_prefixes, checkout_path.join('dev', 'ci', 'mac')
           )
           with self.m.context(env=env, env_prefixes=env_prefixes):
-            self.m.test_utils.run_test(validation, [resource_name])
+            self.m.flutter_bcid.report_stage(BcidStage.COMPILE.value)
+            self.m.test_utils.run_test(
+              validation,
+              [resource_name],
+              timeout_secs=4500 # 75 minutes
+            )
       else:
         # Override LUCI_BRANCH for docs and release candidate branches. Docs built from
         # release candidate branches need to be build as stable to ensure they are processed
@@ -67,16 +73,31 @@
           env['LUCI_CI'] = True
 
         with self.m.context(env=env, env_prefixes=env_prefixes):
-          self.m.test_utils.run_test(validation, [resource_name])
+          self.m.flutter_bcid.report_stage(BcidStage.COMPILE.value)
+          self.m.test_utils.run_test(
+            validation,
+            [resource_name],
+            timeout_secs=4500 # 75 minutes
+          )
           if ((validation == 'docs' or validation == 'docs_deploy') and
               self.m.properties.get('firebase_project')):
             docs_path = checkout_path.join('dev', 'docs')
             # Do not upload on docs_deploy.
             if not validation == 'docs_deploy':
-              self.m.bucket_util.upload_folder('Upload API Docs', docs_path, 'doc', "api_docs.zip")
+              self.m.flutter_bcid.report_stage(BcidStage.UPLOAD.value)
+              src = docs_path.join('api_docs.zip')
+              commit = self.m.repo_util.get_commit(checkout_path)
+              dst = 'gs://flutter_infra_release/flutter/%s/api_docs.zip' % commit
+              self.m.archives.upload_artifact(src, dst)
+              self.m.flutter_bcid.upload_provenance(src, dst)
+              self.m.flutter_bcid.report_stage(BcidStage.UPLOAD_COMPLETE.value)
             project = self.m.properties.get('firebase_project')
             # Only deploy to firebase directly if this is master or main.
-            if (self.m.properties.get('git_branch') in ['master', 'main']):
+            git_ref = self.m.properties.get('release_ref') or self.m.buildbucket.gitiles_commit.ref
+            if ((self.m.properties.get('git_branch') in ['master', 'main']) or
+                (git_ref == 'refs/heads/stable')):
+              # Post-processing of docs require LUCI_BRANCH to be set.
+              env['LUCI_BRANCH'] = 'stable'
               self.m.firebase.deploy_docs(
                   env=env,
                   env_prefixes=env_prefixes,
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/docs.json b/recipe_modules/adhoc_validation/examples/full.expected/docs.json
index c83be1d..b2d374a 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/docs.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/docs.json
@@ -133,7 +133,7 @@
       ]
     },
     "name": "Docs.docs",
-    "timeout": 3600,
+    "timeout": 4500,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@test_stdout@@@@",
@@ -144,9 +144,11 @@
   },
   {
     "cmd": [
-      "python",
-      "RECIPE_MODULE[flutter::zip]/resources/zip.py"
+      "git",
+      "rev-parse",
+      "HEAD"
     ],
+    "cwd": "[START_DIR]/flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -164,8 +166,8 @@
         "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
-    "name": "Docs.Zip doc",
-    "stdin": "{\"entries\": [{\"path\": \"[START_DIR]/flutter sdk/dev/docs/doc\", \"type\": \"dir\"}], \"output\": \"[CLEANUP]/tmp_tmp_1/api_docs.zip\", \"root\": \"[START_DIR]/flutter sdk/dev/docs\"}",
+    "infra_step": true,
+    "name": "Docs.git rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
@@ -177,8 +179,10 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "rmtree",
-      "[CLEANUP]/tmp_tmp_1"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[CLEANUP]/tmp_tmp_1/flutter"
     ],
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
@@ -198,12 +202,83 @@
       ]
     },
     "infra_step": true,
-    "name": "Docs.temp dir for Upload API Docs",
+    "name": "Docs.Ensure flutter",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/flutter sdk/dev/docs/api_docs.zip",
+      "[CLEANUP]/tmp_tmp_1/flutter"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Docs.Copy gs://flutter_infra_release/flutter//api_docs.zip",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-r",
+      "[CLEANUP]/tmp_tmp_1/*",
+      "gs://flutter_infra_release/"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "stable",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Docs.gsutil flutter//api_docs.zip",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gsutil.upload@https://console.cloud.google.com/storage/browser/flutter_infra_release/@@@"
+    ]
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json b/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json
index 3dcf8e6..532d800 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json
@@ -19,7 +19,7 @@
       "Traceback (most recent call last):",
       "  File \"RECIPE_REPO[flutter]/recipe_modules/adhoc_validation/examples/full.py\", line 26, in RunSteps",
       "    api.adhoc_validation.run('Docs', validation, {}, {})",
-      "  File \"RECIPE_REPO[flutter]/recipe_modules/adhoc_validation/api.py\", line 36, in run",
+      "  File \"RECIPE_REPO[flutter]/recipe_modules/adhoc_validation/api.py\", line 37, in run",
       "    raise AssertionError(msg)",
       "AssertionError('invalid is not listed in available_validations.')"
     ]
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/linux.json b/recipe_modules/adhoc_validation/examples/full.expected/linux.json
index e0ce568..5f0b371 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/linux.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/linux.json
@@ -133,7 +133,7 @@
       ]
     },
     "name": "Docs.docs",
-    "timeout": 3600,
+    "timeout": 4500,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@test_stdout@@@@",
@@ -144,9 +144,11 @@
   },
   {
     "cmd": [
-      "python",
-      "RECIPE_MODULE[flutter::zip]/resources/zip.py"
+      "git",
+      "rev-parse",
+      "HEAD"
     ],
+    "cwd": "[START_DIR]/flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "main",
@@ -164,8 +166,8 @@
         "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
-    "name": "Docs.Zip doc",
-    "stdin": "{\"entries\": [{\"path\": \"[START_DIR]/flutter sdk/dev/docs/doc\", \"type\": \"dir\"}], \"output\": \"[CLEANUP]/tmp_tmp_1/api_docs.zip\", \"root\": \"[START_DIR]/flutter sdk/dev/docs\"}",
+    "infra_step": true,
+    "name": "Docs.git rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
@@ -177,8 +179,10 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "rmtree",
-      "[CLEANUP]/tmp_tmp_1"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[CLEANUP]/tmp_tmp_1/flutter"
     ],
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
@@ -198,13 +202,84 @@
       ]
     },
     "infra_step": true,
-    "name": "Docs.temp dir for Upload API Docs",
+    "name": "Docs.Ensure flutter",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
   },
   {
     "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/flutter sdk/dev/docs/api_docs.zip",
+      "[CLEANUP]/tmp_tmp_1/flutter"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "main",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Docs.Copy gs://flutter_infra_release/flutter//api_docs.zip",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-r",
+      "[CLEANUP]/tmp_tmp_1/*",
+      "gs://flutter_infra_release/"
+    ],
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "main",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "Docs.gsutil flutter//api_docs.zip",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gsutil.upload@https://console.cloud.google.com/storage/browser/flutter_infra_release/@@@"
+    ]
+  },
+  {
+    "cmd": [
       "luci-auth",
       "token",
       "-scopes",
@@ -287,7 +362,7 @@
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GCP_PROJECT": "myproject",
       "GIT_BRANCH": "main",
-      "LUCI_BRANCH": "",
+      "LUCI_BRANCH": "stable",
       "LUCI_CI": "True",
       "LUCI_PR": "",
       "OS": "linux",
@@ -316,7 +391,7 @@
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GCP_PROJECT": "myproject",
       "GIT_BRANCH": "main",
-      "LUCI_BRANCH": "",
+      "LUCI_BRANCH": "stable",
       "LUCI_CI": "True",
       "LUCI_PR": "",
       "OS": "linux",
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/mac.json b/recipe_modules/adhoc_validation/examples/full.expected/mac.json
index 320afb3..c32eb18 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/mac.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/mac.json
@@ -236,7 +236,7 @@
       ]
     },
     "name": "Docs.docs",
-    "timeout": 3600,
+    "timeout": 4500,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@test_stdout@@@@",
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/mac_nodeps.json b/recipe_modules/adhoc_validation/examples/full.expected/mac_nodeps.json
index 0eb20ef..4104b3e 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/mac_nodeps.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/mac_nodeps.json
@@ -133,7 +133,7 @@
       ]
     },
     "name": "Docs.docs",
-    "timeout": 3600,
+    "timeout": 4500,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@test_stdout@@@@",
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/win.json b/recipe_modules/adhoc_validation/examples/full.expected/win.json
index 6392e76..edb57b5 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/win.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/win.json
@@ -104,7 +104,7 @@
       ]
     },
     "name": "Docs.docs",
-    "timeout": 3600,
+    "timeout": 4500,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@test_stdout@@@@",
diff --git a/recipe_modules/adhoc_validation/examples/full.py b/recipe_modules/adhoc_validation/examples/full.py
index 8d9b0f6..b45672a 100644
--- a/recipe_modules/adhoc_validation/examples/full.py
+++ b/recipe_modules/adhoc_validation/examples/full.py
@@ -4,16 +4,16 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
-PYTHON_VERSION_COMPATIBILITY = 'PY3'
-
 DEPS = [
     'flutter/adhoc_validation',
     'flutter/repo_util',
+    'recipe_engine/buildbucket',
     'recipe_engine/context',
     'recipe_engine/path',
     'recipe_engine/platform',
     'recipe_engine/properties',
     'recipe_engine/raw_io',
+    'recipe_engine/runtime',
 ]
 
 
diff --git a/recipe_modules/adhoc_validation/resources/verify_binaries_codesigned.sh b/recipe_modules/adhoc_validation/resources/verify_binaries_codesigned.sh
index b3dae12..acf3a66 100644
--- a/recipe_modules/adhoc_validation/resources/verify_binaries_codesigned.sh
+++ b/recipe_modules/adhoc_validation/resources/verify_binaries_codesigned.sh
@@ -14,4 +14,4 @@
   exit 1
 fi
 # Run the actual validation.
-./dev/conductor/bin/conductor codesign --verify --revision $REVISION
+./dev/conductor/bin/conductor codesign --verify --revision $REVISION --upstream='https://github.com/flutter/flutter.git'
diff --git a/recipes/flutter/flutter.expected/validators.json b/recipes/flutter/flutter.expected/validators.json
index 5a4d68a..92bb152 100644
--- a/recipes/flutter/flutter.expected/validators.json
+++ b/recipes/flutter/flutter.expected/validators.json
@@ -259,7 +259,7 @@
       ]
     },
     "name": "dart analyze.analyze",
-    "timeout": 3600,
+    "timeout": 4500,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@test_stdout@@@@",