Add bcid stage reporting to flutter recipes.

API docs use flutter/flutter recipe which was not reporting bcid stages.

Bug: https://github.com/flutter/flutter/issues/117157
Change-Id: Ie6f1f17e4c7f1af298a0ac9ceff892e56cfb5819
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/37520
Reviewed-by: Drew Roen <drewroen@google.com>
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
(cherry picked from commit 978bed882eb266aff4b0242439902e000a7db7e9)
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/37523
Reviewed-by: Xilai Zhang <xilaizhang@google.com>
diff --git a/recipe_modules/flutter_bcid/__init__.py b/recipe_modules/flutter_bcid/__init__.py
new file mode 100644
index 0000000..c790b26
--- /dev/null
+++ b/recipe_modules/flutter_bcid/__init__.py
@@ -0,0 +1,11 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+    'flutter/repo_util',
+    'recipe_engine/bcid_reporter',
+    'recipe_engine/buildbucket',
+    'recipe_engine/file',
+    'recipe_engine/platform',
+]
diff --git a/recipe_modules/flutter_bcid/api.py b/recipe_modules/flutter_bcid/api.py
new file mode 100644
index 0000000..106a26e
--- /dev/null
+++ b/recipe_modules/flutter_bcid/api.py
@@ -0,0 +1,61 @@
+# Copyright 2022 The Chromium 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 re
+from enum import Enum
+
+from recipe_engine import recipe_api
+
+
+class BcidStage(Enum):
+  """Enum representing valid bcis stages."""
+  START='start'
+  FETCH='fetch'
+  COMPILE='compile'
+  UPLOAD='upload'
+  UPLOAD_COMPLETE='upload-complete'
+  TEST='test'
+
+
+class FlutterBcidApi(recipe_api.RecipeApi):
+
+  def is_official_build(self):
+    bucket = self.m.buildbucket.build.builder.bucket
+    # No-op for builders running outside of dart-internal.
+    return bucket == 'flutter'
+
+  def is_prod_build(self):
+    bucket = self.m.buildbucket.build.builder.bucket
+    return bucket == 'prod'
+
+  def report_stage(self, stage):
+    if (self.is_official_build() and
+        # TODO(jseales): Uncomment next line after windows
+        # can generate provenance succesfully
+        # https://github.com/flutter/flutter/issues/116749
+        not self.m.platform.is_win):
+      self.m.bcid_reporter.report_stage(stage)
+
+  def upload_provenance(self, local_artifact_path, remote_artifact_path):
+    """Generate provenance for given artifact.
+
+    This function acts on one specific local file and one specific
+    remote file location. It does not accept glob patterns or
+    directories.
+
+    parmeters:
+      local_artifact_path: (str) path and filename of a specific file.
+      remote_artifact_path: (str) path and filename of a specific file.
+    """
+    if (self.is_official_build() and 
+      # TODO(jseales): Uncomment next line after windows
+      # can generate provenance succesfully
+      # https://github.com/flutter/flutter/issues/116749
+      not self.m.platform.is_win):
+      sha256 = self.m.file.file_hash(local_artifact_path)
+      self.m.bcid_reporter.report_gcs(
+          sha256,
+          remote_artifact_path
+      )
+
diff --git a/recipe_modules/flutter_bcid/examples/full.expected/basic.json b/recipe_modules/flutter_bcid/examples/full.expected/basic.json
new file mode 100644
index 0000000..4560cd4
--- /dev/null
+++ b/recipe_modules/flutter_bcid/examples/full.expected/basic.json
@@ -0,0 +1,117 @@
+[
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/reporter",
+      "-ensure-file",
+      "infra/tools/security/provenance_broker/${platform} git_revision:85c2329aae7547b83c1c4908a05b5f093f3f93f5",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-git_revision:85c\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/security/provenance_broker/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/reporter/snoopy_broker",
+      "-report-stage",
+      "-stage",
+      "one"
+    ],
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "report_stage"
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "file_hash",
+      "[CACHE]/file.zip"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Compute file hash",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Hash calculated: d2162c0c57d337899c660e7a10c51b65e3a82f5ed56723534aae911871dd4b20@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/reporter/snoopy_broker",
+      "-report-gcs",
+      "-digest",
+      "d2162c0c57d337899c660e7a10c51b65e3a82f5ed56723534aae911871dd4b20",
+      "-gcs-uri",
+      "gs://bucket/final_path/file.txt"
+    ],
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "report_gcs"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/flutter_bcid/examples/full.expected/prod_build.json b/recipe_modules/flutter_bcid/examples/full.expected/prod_build.json
new file mode 100644
index 0000000..4560cd4
--- /dev/null
+++ b/recipe_modules/flutter_bcid/examples/full.expected/prod_build.json
@@ -0,0 +1,117 @@
+[
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/reporter",
+      "-ensure-file",
+      "infra/tools/security/provenance_broker/${platform} git_revision:85c2329aae7547b83c1c4908a05b5f093f3f93f5",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-git_revision:85c\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/security/provenance_broker/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/reporter/snoopy_broker",
+      "-report-stage",
+      "-stage",
+      "one"
+    ],
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "report_stage"
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "file_hash",
+      "[CACHE]/file.zip"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Compute file hash",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Hash calculated: d2162c0c57d337899c660e7a10c51b65e3a82f5ed56723534aae911871dd4b20@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/reporter/snoopy_broker",
+      "-report-gcs",
+      "-digest",
+      "d2162c0c57d337899c660e7a10c51b65e3a82f5ed56723534aae911871dd4b20",
+      "-gcs-uri",
+      "gs://bucket/final_path/file.txt"
+    ],
+    "luci_context": {
+      "realm": {
+        "name": "dart-internal:flutter"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "report_gcs"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/flutter_bcid/examples/full.py b/recipe_modules/flutter_bcid/examples/full.py
new file mode 100644
index 0000000..b21b673
--- /dev/null
+++ b/recipe_modules/flutter_bcid/examples/full.py
@@ -0,0 +1,41 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+    'flutter/flutter_bcid',
+    'recipe_engine/buildbucket',
+    'recipe_engine/path',
+]
+
+
+def RunSteps(api):
+  api.flutter_bcid.report_stage('one')
+  api.flutter_bcid.upload_provenance(
+      api.path['cache'].join('file.zip'),
+      'gs://bucket/final_path/file.txt'
+  )
+  api.flutter_bcid.is_official_build()
+  api.flutter_bcid.is_prod_build()
+
+
+def GenTests(api):
+  yield api.test(
+      'basic',
+      api.buildbucket.ci_build(
+          project='dart-internal',
+          bucket='flutter',
+          git_repo='https://dart.googlesource.com/monorepo',
+          git_ref='refs/heads/main'
+      ),
+  )
+
+  yield api.test(
+      'prod_build',
+      api.buildbucket.ci_build(
+          project='dart-internal',
+          bucket='flutter',
+          git_repo='https://dart.googlesource.com/monorepo',
+          git_ref='refs/heads/main'
+      ),
+  )
diff --git a/recipes/flutter/flutter.expected/validators.json b/recipes/flutter/flutter.expected/validators.json
index 9f3f0de..5a4d68a 100644
--- a/recipes/flutter/flutter.expected/validators.json
+++ b/recipes/flutter/flutter.expected/validators.json
@@ -28,7 +28,7 @@
       "-u",
       "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
       "--path",
-      "[START_DIR]/flutter sdk",
+      "[START_DIR]/\u00c1 flutter sdk",
       "--url",
       "https://flutter.googlesource.com/mirrors/flutter"
     ],
@@ -47,7 +47,7 @@
       "--progress",
       "--tags"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "PATH": "RECIPE_REPO[depot_tools]:<PATH>"
     },
@@ -64,7 +64,7 @@
       "-f",
       "FETCH_HEAD"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "infra_step": true,
     "name": "checkout source code.Checkout flutter/flutter.git checkout",
     "~followup_annotations": [
@@ -77,7 +77,7 @@
       "rev-parse",
       "HEAD"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "infra_step": true,
     "name": "checkout source code.Checkout flutter/flutter.read revision",
     "~followup_annotations": [
@@ -94,7 +94,7 @@
       "-d",
       "-x"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "infra_step": true,
     "name": "checkout source code.Checkout flutter/flutter.git clean",
     "~followup_annotations": [
@@ -107,7 +107,7 @@
       "submodule",
       "sync"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "infra_step": true,
     "name": "checkout source code.Checkout flutter/flutter.submodule sync",
     "~followup_annotations": [
@@ -122,7 +122,7 @@
       "--init",
       "--recursive"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "infra_step": true,
     "name": "checkout source code.Checkout flutter/flutter.submodule update",
     "~followup_annotations": [
@@ -135,7 +135,7 @@
       "rev-parse",
       "HEAD"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "infra_step": true,
     "name": "git rev-parse"
   },
@@ -148,7 +148,7 @@
       "flutter",
       "doctor"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -158,12 +158,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "name": "prepare environment.flutter doctor",
@@ -176,7 +176,7 @@
       "flutter",
       "update-packages"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -186,12 +186,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -210,7 +210,7 @@
       "755",
       "RECIPE_MODULE[flutter::adhoc_validation]/resources/analyze.sh"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -220,12 +220,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -238,7 +238,7 @@
     "cmd": [
       "RECIPE_MODULE[flutter::adhoc_validation]/resources/analyze.sh"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -248,14 +248,14 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin",
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "name": "dart analyze.analyze",
@@ -277,7 +277,7 @@
       "pkill",
       "chrome"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -287,12 +287,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -306,7 +306,7 @@
       "pkill",
       "dart"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -316,12 +316,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -335,7 +335,7 @@
       "pkill",
       "flutter"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -345,12 +345,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -364,7 +364,7 @@
       "pkill",
       "java"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -374,12 +374,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -393,7 +393,7 @@
       "pkill",
       "adb"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -403,12 +403,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
@@ -426,7 +426,7 @@
       "-o",
       "%MEM"
     ],
-    "cwd": "[START_DIR]/flutter sdk",
+    "cwd": "[START_DIR]/\u00c1 flutter sdk",
     "env": {
       "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
       "GIT_BRANCH": "",
@@ -436,12 +436,12 @@
       "OS": "linux",
       "PUB_CACHE": "[START_DIR]/.pub-cache",
       "REVISION": "",
-      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+      "SDK_CHECKOUT_PATH": "[START_DIR]/\u00c1 flutter sdk"
     },
     "env_prefixes": {
       "PATH": [
-        "[START_DIR]/flutter sdk/bin",
-        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+        "[START_DIR]/\u00c1 flutter sdk/bin",
+        "[START_DIR]/\u00c1 flutter sdk/bin/cache/dart-sdk/bin"
       ]
     },
     "infra_step": true,
diff --git a/recipes/flutter/flutter.py b/recipes/flutter/flutter.py
index be6ea27..1c1cb2a 100644
--- a/recipes/flutter/flutter.py
+++ b/recipes/flutter/flutter.py
@@ -9,6 +9,7 @@
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
 from PB.go.chromium.org.luci.buildbucket.proto \
   import builds_service as builds_service_pb2
+from RECIPE_MODULES.flutter.flutter_bcid.api import BcidStage
 from google.protobuf import struct_pb2
 
 PYTHON_VERSION_COMPATIBILITY = 'PY3'
@@ -16,6 +17,7 @@
 DEPS = [
     'flutter/adhoc_validation',
     'flutter/display_util',
+    'flutter/flutter_bcid',
     'flutter/flutter_deps',
     'flutter/os_utils',
     'flutter/repo_util',
@@ -30,13 +32,16 @@
 
 def RunSteps(api):
   """Recipe to run flutter sdk tests."""
+  api.flutter_bcid.report_stage(BcidStage.START.value)
   # Collect memory/cpu/process before task execution.
   api.os_utils.collect_os_info()
   api.os_utils.print_pub_certs()
 
   # Trigger validation tests. This is to optimize resources usage
   # when don't need to run in shards.
-  checkout_path = api.path['start_dir'].join('flutter sdk')
+  # include UTF-8 char in path to test for resilience
+  checkout_path = api.path['start_dir'].join('Á flutter sdk')
+  api.flutter_bcid.report_stage(BcidStage.FETCH.value)
   with api.step.nest('checkout source code'):
     api.repo_util.checkout(
         'flutter',
@@ -81,4 +86,4 @@
           android_sdk_license='android_license',
           android_sdk_preview_license='android_preview_license'
       ), api.repo_util.flutter_environment_data()
-  )
\ No newline at end of file
+  )
diff --git a/recipes/flutter/flutter_drone.py b/recipes/flutter/flutter_drone.py
index 7cb231c..f8aaad0 100644
--- a/recipes/flutter/flutter_drone.py
+++ b/recipes/flutter/flutter_drone.py
@@ -10,8 +10,12 @@
 
 PYTHON_VERSION_COMPATIBILITY = 'PY3'
 
+from RECIPE_MODULES.flutter.flutter_bcid.api import BcidStage
+
 DEPS = [
+    'flutter/flutter_bcid',
     'flutter/flutter_deps',
+    'flutter/logs_util',
     'flutter/os_utils',
     'flutter/osx_sdk',
     'flutter/repo_util',
@@ -55,11 +59,13 @@
 
 
 def RunSteps(api):
+  api.flutter_bcid.report_stage(BcidStage.START.value)
   # Collect memory/cpu/process before task execution.
   api.os_utils.collect_os_info()
   api.os_utils.print_pub_certs()
 
   checkout_path = api.path['start_dir'].join('flutter')
+  api.flutter_bcid.report_stage(BcidStage.FETCH.value)
   api.repo_util.checkout(
       'flutter',
       checkout_path=checkout_path,