Fork gsutil module from fuchsia recipes.
Bug:https://github.com/flutter/flutter/issues/120248
Change-Id: Ib1c08b7a51b00b503a8eb3aa05033a2bf91c0976
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/38802
Reviewed-by: Nehal Patel <nehalvpatel@google.com>
Reviewed-by: Godofredo Contreras <godofredoc@google.com>
Commit-Queue: Yusuf Mohsinally <mohsinally@google.com>
diff --git a/recipe_modules/gsutil/__init__.py b/recipe_modules/gsutil/__init__.py
new file mode 100644
index 0000000..c224c00
--- /dev/null
+++ b/recipe_modules/gsutil/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2020 The Fuchsia 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 = [
+ "fuchsia/buildbucket_util",
+ "fuchsia/ensure_tool",
+ "fuchsia/python3",
+ "fuchsia/utils",
+ "recipe_engine/platform",
+ "recipe_engine/step",
+ "recipe_engine/time",
+ "recipe_engine/url",
+]
diff --git a/recipe_modules/gsutil/api.py b/recipe_modules/gsutil/api.py
new file mode 100644
index 0000000..d5ce30d
--- /dev/null
+++ b/recipe_modules/gsutil/api.py
@@ -0,0 +1,289 @@
+# Copyright 2016 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.
+
+from recipe_engine import recipe_api
+
+
+class GSUtilApi(recipe_api.RecipeApi):
+ """GSUtilApi provides support for GSUtil."""
+
+ @recipe_api.non_step
+ def join(self, *parts):
+ """Constructs a GS path from composite parts."""
+ return "/".join(p.strip("/") for p in parts)
+
+ def upload_namespaced_file(
+ self,
+ source,
+ bucket,
+ subpath,
+ namespace=None,
+ metadata=None,
+ no_clobber=True,
+ unauthenticated_url=False,
+ **kwargs,
+ ):
+ """Uploads a file to GCS under a subpath specific to the given build.
+
+ Will upload the file to:
+ gs://<bucket>/<build id>/<subpath or basename of file>
+
+ Args:
+ source (Path): A path to the file to upload.
+ bucket (str): The name of the GCS bucket to upload to.
+ subpath (str): The end of the destination path within the
+ build-specific subdirectory.
+ namespace (str or None): A unique ID for this build. Defaults to the
+ current build ID or led run ID.
+ metadata (dict): A dictionary of metadata values to upload along
+ with the file.
+ no_clobber (bool): Skip upload if destination path already exists in
+ GCS.
+ unauthenticated_url (bool): Whether to present a URL that requires
+ no authentication in the GCP web UI.
+ """
+ kwargs.setdefault("link_name", subpath)
+ return self.upload(
+ bucket=bucket,
+ src=source,
+ dst=self.namespaced_gcs_path(subpath, namespace),
+ metadata=metadata,
+ no_clobber=no_clobber,
+ unauthenticated_url=unauthenticated_url,
+ name=f"upload {subpath} to {bucket}",
+ **kwargs,
+ )
+
+ def upload_namespaced_directory(
+ self, source, bucket, subpath, namespace=None, rsync=True, **kwargs
+ ):
+ """Uploads a directory to GCS under a subpath specific to the given build.
+
+ Will upload the directory to:
+ gs://<bucket>/<build id>/<subpath>
+
+ Args:
+ source (Path): A path to the file to upload.
+ bucket (str): The name of the GCS bucket to upload to.
+ subpath (str): The end of the destination path within the
+ build-specific subdirectory.
+ namespace (str or None): A unique ID for this build. Defaults to the
+ current build ID or led run ID.
+ rsync (bool): Whether to use rsync, which is idempotent but
+ sometimes less reliable.
+ """
+ kwargs.setdefault("link_name", subpath)
+ func = self.upload
+ if rsync:
+ func = self.rsync
+ return func(
+ bucket=bucket,
+ src=source,
+ dst=self.namespaced_gcs_path(subpath, namespace),
+ recursive=True,
+ multithreaded=True,
+ no_clobber=True,
+ name=f"upload {subpath} to {bucket}",
+ **kwargs,
+ )
+
+ def namespaced_gcs_path(self, relative_path, namespace=None):
+ if not namespace:
+ namespace = self.m.buildbucket_util.id
+ return f"builds/{namespace}/{relative_path}"
+
+ def http_url(self, bucket, dest, unauthenticated_url=False):
+ base = (
+ "https://storage.googleapis.com"
+ if unauthenticated_url
+ else "https://storage.cloud.google.com"
+ )
+ return f"{base}/{bucket}/{self.m.url.quote(dest)}"
+
+ def _directory_listing_url(self, bucket, dest):
+ """Returns the URL for a GCS bucket subdirectory listing in the GCP console."""
+ return (
+ f"https://console.cloud.google.com/storage/browser/{bucket}/"
+ f"{self.m.url.quote(dest)}"
+ )
+
+ def namespaced_directory_url(self, bucket, subpath="", namespace=None):
+ return self._directory_listing_url(
+ bucket,
+ self.namespaced_gcs_path(subpath, namespace),
+ )
+
+ @staticmethod
+ def _get_metadata_field(name, provider_prefix=None):
+ """Returns: (str) the metadata field to use with Google Storage
+
+ The Google Storage specification for metadata can be found at:
+ https://developers.google.com/storage/docs/gsutil/addlhelp/WorkingWithObjectMetadata
+ """
+ # Already contains custom provider prefix
+ if name.lower().startswith("x-"):
+ return name
+
+ # See if it's innately supported by Google Storage
+ if name in (
+ "Cache-Control",
+ "Content-Disposition",
+ "Content-Encoding",
+ "Content-Language",
+ "Content-MD5",
+ "Content-Type",
+ "Custom-Time",
+ ):
+ return name
+
+ # Add provider prefix
+ if not provider_prefix:
+ provider_prefix = "x-goog-meta"
+ return f"{provider_prefix}-{name}"
+
+ @staticmethod
+ def unauthenticated_url(url):
+ """Transform an authenticated URL to an unauthenticated URL."""
+ return url.replace(
+ "https://storage.cloud.google.com/", "https://storage.googleapis.com/"
+ )
+
+ def _add_custom_time(self, metadata):
+ if not metadata:
+ metadata = {}
+ metadata["Custom-Time"] = self.m.time.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
+ return metadata
+
+ def upload(
+ self,
+ bucket,
+ src,
+ dst,
+ link_name="gsutil.upload",
+ unauthenticated_url=False,
+ recursive=False,
+ no_clobber=False,
+ gzip_exts=(),
+ **kwargs,
+ ):
+ kwargs["metadata"] = self._add_custom_time(kwargs.pop("metadata", {}))
+ args = ["cp"]
+ if recursive:
+ args.append("-r")
+ if no_clobber:
+ args.append("-n")
+ if gzip_exts:
+ args.extend(["-j"] + gzip_exts)
+ args.extend([src, f"gs://{bucket}/{dst}"])
+ if not recursive or no_clobber:
+ # gsutil supports resumable uploads if we run the same command
+ # again, but it's only safe to resume uploading if we're only
+ # uploading a single file, or if we're operating in no_clobber mode.
+ step = self.m.utils.retry(
+ lambda: self._run(*args, **kwargs),
+ max_attempts=3,
+ )
+ else:
+ step = self._run(*args, **kwargs)
+ if link_name:
+ link_url = self.http_url(
+ bucket, dst, unauthenticated_url=unauthenticated_url
+ )
+ step.presentation.links[link_name] = link_url
+ return step
+
+ def rsync(
+ self,
+ bucket,
+ src,
+ dst,
+ link_name="gsutil.rsync",
+ recursive=True,
+ no_clobber=False,
+ gzip_exts=(),
+ **kwargs,
+ ):
+ kwargs["metadata"] = self._add_custom_time(kwargs.pop("metadata", {}))
+ args = ["rsync"]
+ if recursive:
+ args.append("-r")
+ if no_clobber:
+ # This will skip files already existing in dst with a later
+ # timestamp.
+ args.append("-u")
+ if gzip_exts:
+ args.extend(["-j"] + gzip_exts)
+ args.extend([src, f"gs://{bucket}/{dst}"])
+ step = self.m.utils.retry(lambda: self._run(*args, **kwargs), max_attempts=3)
+ if link_name:
+ link_url = self._directory_listing_url(bucket, dst)
+ step.presentation.links[link_name] = link_url
+ return step
+
+ def copy(
+ self,
+ src_bucket,
+ src,
+ dst_bucket,
+ dst,
+ link_name="gsutil.copy",
+ unauthenticated_url=False,
+ recursive=False,
+ **kwargs,
+ ):
+ args = ["cp"]
+ if recursive:
+ args.append("-r")
+ args.extend([f"gs://{src_bucket}/{src}", f"gs://{dst_bucket}/{dst}"])
+ step = self._run(*args, **kwargs)
+ if link_name:
+ step.presentation.links[link_name] = self.http_url(
+ dst_bucket, dst, unauthenticated_url=unauthenticated_url
+ )
+ return step
+
+ def download(self, src_bucket, src, dest, recursive=False, **kwargs):
+ """Downloads gcs bucket file to local disk.
+
+ Args:
+ src_bucket (str): gcs bucket name.
+ src (str): gcs file or path name.
+ recursive (bool): bool to indicate to copy recursively.
+ dest (str): local file path root to copy to.
+ """
+ args = ["cp"]
+ if recursive:
+ args.append("-r")
+ args.extend([f"gs://{src_bucket}/{src}", dest])
+ return self._run(*args, **kwargs)
+
+ @property
+ def _gsutil_tool(self):
+ return self.m.ensure_tool("gsutil", self.resource("tool_manifest.json"))
+
+ def _run(self, *args, **kwargs):
+ """Return a step to run arbitrary gsutil command."""
+ assert self._gsutil_tool
+ name = kwargs.pop("name", "gsutil " + args[0])
+ infra_step = kwargs.pop("infra_step", True)
+ cmd_prefix = [self._gsutil_tool]
+ # Note that metadata arguments have to be passed before the command.
+ metadata = kwargs.pop("metadata", [])
+ if metadata:
+ for k, v in sorted(metadata.items()):
+ field = self._get_metadata_field(k)
+ param = (field) if v is None else (f"{field}:{v}")
+ cmd_prefix.extend(["-h", param])
+ options = kwargs.pop("options", {})
+ options["software_update_check_period"] = 0
+ if options:
+ for k, v in sorted(options.items()):
+ cmd_prefix.extend(["-o", f"GSUtil:{k}={v}"])
+ if kwargs.pop("multithreaded", False):
+ cmd_prefix.extend(["-m"])
+
+ # The `gsutil` executable is a Python script with a shebang, and Windows
+ # doesn't support shebangs so we have to run it via Python.
+ step_func = self.m.python3 if self.m.platform.is_win else self.m.step
+ return step_func(name, cmd_prefix + list(args), infra_step=infra_step, **kwargs)
diff --git a/recipe_modules/gsutil/resources/tool_manifest.json b/recipe_modules/gsutil/resources/tool_manifest.json
new file mode 100644
index 0000000..6faf032
--- /dev/null
+++ b/recipe_modules/gsutil/resources/tool_manifest.json
@@ -0,0 +1,5 @@
+{
+ "path": "infra/3pp/tools/gsutil",
+ "version": "version:2@5.19",
+ "do_not_autoroll": true
+}
diff --git a/recipe_modules/gsutil/tests/full.expected/basic.json b/recipe_modules/gsutil/tests/full.expected/basic.json
new file mode 100644
index 0000000..cdfcb1a
--- /dev/null
+++ b/recipe_modules/gsutil/tests/full.expected/basic.json
@@ -0,0 +1,313 @@
+[
+ {
+ "cmd": [],
+ "name": "ensure gsutil"
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure gsutil.read manifest",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@{@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@ \"path\": \"path/to/gsutil\",@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@ \"version\": \"version:pinned-version\"@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@}@@@",
+ "@@@STEP_LOG_END@tool_manifest.json@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "ensure gsutil.install path/to/gsutil",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "ensure-directory",
+ "--mode",
+ "0777",
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure gsutil.install path/to/gsutil.ensure package directory",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "ensure",
+ "-root",
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version",
+ "-ensure-file",
+ "path/to/gsutil version:pinned-version",
+ "-max-threads",
+ "0",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure gsutil.install path/to/gsutil.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@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-version:pinned-v\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"path/to/gsutil\"@@@",
+ "@@@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]/cipd_tool/path/to/gsutil/version%3Apinned-version/gsutil",
+ "-h",
+ "Cache-Control:no-cache",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:21.500000Z",
+ "-h",
+ "x-goog-meta-Remove-Me",
+ "-h",
+ "x-goog-meta-Test-Field:value",
+ "-h",
+ "x-custom-field:custom-value",
+ "-o",
+ "GSUtil:parallel_composite_upload_threshold=50M",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-n",
+ "example",
+ "gs://[CLEANUP]/file/builds/8945511751514863184/path/to/file"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload path/to/file to [CLEANUP]/file",
+ "~followup_annotations": [
+ "@@@STEP_LINK@path/to/file@https://storage.googleapis.com/[CLEANUP]/file/builds/8945511751514863184/path/to/file@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version/gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:23.000000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "-m",
+ "rsync",
+ "-r",
+ "-u",
+ "-j",
+ "html",
+ "[CLEANUP]/dir",
+ "gs://example/builds/8945511751514863184/rsync_subpath"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload rsync_subpath to example",
+ "~followup_annotations": [
+ "@@@STEP_LINK@rsync_subpath@https://console.cloud.google.com/storage/browser/example/builds/8945511751514863184/rsync_subpath@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version/gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:24.500000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "-m",
+ "cp",
+ "-r",
+ "-n",
+ "-j",
+ "html",
+ "[CLEANUP]/dir",
+ "gs://example/builds/8945511751514863184/cp_subpath"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload cp_subpath to example",
+ "~followup_annotations": [
+ "@@@STEP_LINK@cp_subpath@https://storage.cloud.google.com/example/builds/8945511751514863184/cp_subpath@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version/gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:26.000000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-r",
+ "[CLEANUP]/dir",
+ "gs://example/dir"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "gsutil cp",
+ "~followup_annotations": [
+ "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/example/dir@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version/gsutil",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-r",
+ "gs://example/foo",
+ "gs://example/bar"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "gsutil cp (2)",
+ "~followup_annotations": [
+ "@@@STEP_LINK@gsutil.copy@https://storage.cloud.google.com/example/bar@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/path/to/gsutil/version%3Apinned-version/gsutil",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-r",
+ "gs://example/foo",
+ "tmp/"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "gsutil cp (3)"
+ },
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipe_modules/gsutil/tests/full.expected/retry_on_failure.json b/recipe_modules/gsutil/tests/full.expected/retry_on_failure.json
new file mode 100644
index 0000000..2b7aefc
--- /dev/null
+++ b/recipe_modules/gsutil/tests/full.expected/retry_on_failure.json
@@ -0,0 +1,471 @@
+[
+ {
+ "cmd": [],
+ "name": "ensure gsutil"
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "RECIPE_MODULE[flutter::gsutil]\\resources\\tool_manifest.json",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure gsutil.read manifest",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@{@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@ \"path\": \"path/to/gsutil\",@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@ \"version\": \"version:pinned-version\"@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@}@@@",
+ "@@@STEP_LOG_END@tool_manifest.json@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "ensure gsutil.install path/to/gsutil",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "ensure-directory",
+ "--mode",
+ "0777",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure gsutil.install path/to/gsutil.ensure package directory",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version",
+ "-ensure-file",
+ "path/to/gsutil version:pinned-version",
+ "-max-threads",
+ "0",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure gsutil.install path/to/gsutil.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@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-version:pinned-v\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"path/to/gsutil\"@@@",
+ "@@@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": [],
+ "name": "ensure cpython3"
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "RECIPE_MODULE[fuchsia::python3]\\resources\\tool_manifest.json",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure cpython3.read manifest",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@{@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@ \"path\": \"path/to/cpython3\",@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@ \"version\": \"version:pinned-version\"@@@",
+ "@@@STEP_LOG_LINE@tool_manifest.json@}@@@",
+ "@@@STEP_LOG_END@tool_manifest.json@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "ensure cpython3.install path/to/cpython3",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "ensure-directory",
+ "--mode",
+ "0777",
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure cpython3.install path/to/cpython3.ensure package directory",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version",
+ "-ensure-file",
+ "path/to/cpython3 version:pinned-version",
+ "-max-threads",
+ "0",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "ensure cpython3.install path/to/cpython3.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@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-version:pinned-v\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"path/to/cpython3\"@@@",
+ "@@@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]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-h",
+ "Cache-Control:no-cache",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:21.500000Z",
+ "-h",
+ "x-goog-meta-Remove-Me",
+ "-h",
+ "x-goog-meta-Test-Field:value",
+ "-h",
+ "x-custom-field:custom-value",
+ "-o",
+ "GSUtil:parallel_composite_upload_threshold=50M",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-n",
+ "example",
+ "gs://[CLEANUP]\\file/builds/8945511751514863184/path/to/file"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload path/to/file to [CLEANUP]\\file",
+ "~followup_annotations": [
+ "@@@STEP_LINK@path/to/file@https://storage.googleapis.com/[CLEANUP]\\file/builds/8945511751514863184/path/to/file@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:23.000000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "-m",
+ "rsync",
+ "-r",
+ "-u",
+ "-j",
+ "html",
+ "[CLEANUP]\\dir",
+ "gs://example/builds/8945511751514863184/rsync_subpath"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload rsync_subpath to example",
+ "~followup_annotations": [
+ "@@@STEP_LINK@rsync_subpath@https://console.cloud.google.com/storage/browser/example/builds/8945511751514863184/rsync_subpath@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:24.500000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "-m",
+ "cp",
+ "-r",
+ "-n",
+ "-j",
+ "html",
+ "[CLEANUP]\\dir",
+ "gs://example/builds/8945511751514863184/cp_subpath"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload cp_subpath to example",
+ "~followup_annotations": [
+ "@@@STEP_EXCEPTION@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:24.500000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "-m",
+ "cp",
+ "-r",
+ "-n",
+ "-j",
+ "html",
+ "[CLEANUP]\\dir",
+ "gs://example/builds/8945511751514863184/cp_subpath"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "upload cp_subpath to example (2)",
+ "~followup_annotations": [
+ "@@@STEP_LINK@cp_subpath@https://storage.cloud.google.com/example/builds/8945511751514863184/cp_subpath@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-h",
+ "Custom-Time:2012-05-14T12:53:26.000000Z",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-r",
+ "[CLEANUP]\\dir",
+ "gs://example/dir"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "gsutil cp",
+ "~followup_annotations": [
+ "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/example/dir@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-r",
+ "gs://example/foo",
+ "gs://example/bar"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "gsutil cp (2)",
+ "~followup_annotations": [
+ "@@@STEP_LINK@gsutil.copy@https://storage.cloud.google.com/example/bar@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\path\\to\\cpython3\\version%3Apinned-version\\bin\\python3.exe",
+ "[START_DIR]\\cipd_tool\\path\\to\\gsutil\\version%3Apinned-version\\gsutil",
+ "-o",
+ "GSUtil:software_update_check_period=0",
+ "cp",
+ "-r",
+ "gs://example/foo",
+ "tmp/"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "fuchsia:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "gsutil cp (3)"
+ },
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipe_modules/gsutil/tests/full.py b/recipe_modules/gsutil/tests/full.py
new file mode 100644
index 0000000..2b9e291
--- /dev/null
+++ b/recipe_modules/gsutil/tests/full.py
@@ -0,0 +1,61 @@
+# Copyright 2013 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 = [
+ "fuchsia/buildbucket_util",
+ "flutter/gsutil",
+ "recipe_engine/path",
+ "recipe_engine/platform",
+]
+
+BUCKET = "example"
+
+
+def RunSteps(api):
+ api.gsutil.upload_namespaced_file(
+ BUCKET,
+ api.path["cleanup"].join("file"),
+ api.gsutil.join("path", "to", "file"),
+ metadata={
+ "Test-Field": "value",
+ "Remove-Me": None,
+ "x-custom-field": "custom-value",
+ "Cache-Control": "no-cache",
+ },
+ unauthenticated_url=True,
+ options={"parallel_composite_upload_threshold": "50M"},
+ )
+
+ api.gsutil.upload_namespaced_directory(
+ api.path["cleanup"].join("dir"),
+ BUCKET,
+ "rsync_subpath",
+ gzip_exts=["html"],
+ )
+ api.gsutil.upload_namespaced_directory(
+ api.path["cleanup"].join("dir"),
+ BUCKET,
+ "cp_subpath",
+ rsync=False,
+ gzip_exts=["html"],
+ )
+ api.gsutil.upload(BUCKET, api.path["cleanup"].join("dir"), "dir", recursive=True)
+
+ api.gsutil.copy(BUCKET, "foo", BUCKET, "bar", recursive=True)
+ api.gsutil.download(BUCKET, "foo", "tmp/", recursive=True)
+
+ api.gsutil.unauthenticated_url("https://storage.cloud.google.com/foo/bar")
+
+ dir_url = api.gsutil.namespaced_directory_url("bucket", "foo")
+ assert dir_url.endswith("builds/8945511751514863184/foo"), dir_url
+
+
+def GenTests(api):
+ yield api.buildbucket_util.test("basic")
+ yield (
+ api.buildbucket_util.test("retry_on_failure")
+ # Cover the windows-specific codepath.
+ + api.platform.name("win")
+ + api.step_data(f"upload cp_subpath to {BUCKET}", retcode=1)
+ )
diff --git a/recipe_modules/sdk/__init__.py b/recipe_modules/sdk/__init__.py
index 9e90d46..97e6bd5 100644
--- a/recipe_modules/sdk/__init__.py
+++ b/recipe_modules/sdk/__init__.py
@@ -3,7 +3,7 @@
# found in the LICENSE file.
DEPS = [
- 'fuchsia/gsutil',
+ 'flutter/gsutil',
'flutter/tar',
'recipe_engine/buildbucket',
'recipe_engine/context',
diff --git a/recipe_modules/sdk/examples/full.expected/ensure_arm_sdk.json b/recipe_modules/sdk/examples/full.expected/ensure_arm_sdk.json
index 0ab686b..1731296 100644
--- a/recipe_modules/sdk/examples/full.expected/ensure_arm_sdk.json
+++ b/recipe_modules/sdk/examples/full.expected/ensure_arm_sdk.json
@@ -53,7 +53,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"infra_step": true,
diff --git a/recipe_modules/sdk/examples/full.expected/ensure_intel_sdk.json b/recipe_modules/sdk/examples/full.expected/ensure_intel_sdk.json
index 698e91a..eb4739a 100644
--- a/recipe_modules/sdk/examples/full.expected/ensure_intel_sdk.json
+++ b/recipe_modules/sdk/examples/full.expected/ensure_intel_sdk.json
@@ -53,7 +53,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"infra_step": true,
diff --git a/recipe_modules/sdk/examples/full.expected/has_cache_sdk.json b/recipe_modules/sdk/examples/full.expected/has_cache_sdk.json
index ac23543..d2da765 100644
--- a/recipe_modules/sdk/examples/full.expected/has_cache_sdk.json
+++ b/recipe_modules/sdk/examples/full.expected/has_cache_sdk.json
@@ -119,7 +119,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"infra_step": true,
diff --git a/recipe_modules/sdk/examples/full.expected/missing_package_file.json b/recipe_modules/sdk/examples/full.expected/missing_package_file.json
index 357dcfb..8e9ab50 100644
--- a/recipe_modules/sdk/examples/full.expected/missing_package_file.json
+++ b/recipe_modules/sdk/examples/full.expected/missing_package_file.json
@@ -119,7 +119,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"infra_step": true,
diff --git a/recipe_modules/vdl/examples/full.expected/ensure_vdl.json b/recipe_modules/vdl/examples/full.expected/ensure_vdl.json
index e96c7e2..3c75b5f 100644
--- a/recipe_modules/vdl/examples/full.expected/ensure_vdl.json
+++ b/recipe_modules/vdl/examples/full.expected/ensure_vdl.json
@@ -226,7 +226,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"infra_step": true,
diff --git a/recipes/engine/femu_test.expected/arm64_emulator_arch.json b/recipes/engine/femu_test.expected/arm64_emulator_arch.json
index 14739a4..8d70459 100644
--- a/recipes/engine/femu_test.expected/arm64_emulator_arch.json
+++ b/recipes/engine/femu_test.expected/arm64_emulator_arch.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/dangerous_test_commands.json b/recipes/engine/femu_test.expected/dangerous_test_commands.json
index 51885ea..b9f8653 100644
--- a/recipes/engine/femu_test.expected/dangerous_test_commands.json
+++ b/recipes/engine/femu_test.expected/dangerous_test_commands.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/femu_with_package_list.json b/recipes/engine/femu_test.expected/femu_with_package_list.json
index e846bff..d19a0b9 100644
--- a/recipes/engine/femu_test.expected/femu_with_package_list.json
+++ b/recipes/engine/femu_test.expected/femu_with_package_list.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/invalid_emulator_arch.json b/recipes/engine/femu_test.expected/invalid_emulator_arch.json
index 7e0d2d1..ebb06c5 100644
--- a/recipes/engine/femu_test.expected/invalid_emulator_arch.json
+++ b/recipes/engine/femu_test.expected/invalid_emulator_arch.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/multiple_non_root_fars.json b/recipes/engine/femu_test.expected/multiple_non_root_fars.json
index 2e8db87..e86ce1e 100644
--- a/recipes/engine/femu_test.expected/multiple_non_root_fars.json
+++ b/recipes/engine/femu_test.expected/multiple_non_root_fars.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/no_zircon_file.json b/recipes/engine/femu_test.expected/no_zircon_file.json
index 367cb58..ee3a0fe 100644
--- a/recipes/engine/femu_test.expected/no_zircon_file.json
+++ b/recipes/engine/femu_test.expected/no_zircon_file.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/run_on_test_specified_arch.json b/recipes/engine/femu_test.expected/run_on_test_specified_arch.json
index bdc3667..16d8455 100644
--- a/recipes/engine/femu_test.expected/run_on_test_specified_arch.json
+++ b/recipes/engine/femu_test.expected/run_on_test_specified_arch.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/run_with_dart_aot_behavior.json b/recipes/engine/femu_test.expected/run_with_dart_aot_behavior.json
index dc6b2af..66df4b5 100644
--- a/recipes/engine/femu_test.expected/run_with_dart_aot_behavior.json
+++ b/recipes/engine/femu_test.expected/run_with_dart_aot_behavior.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",
diff --git a/recipes/engine/femu_test.expected/start_femu.json b/recipes/engine/femu_test.expected/start_femu.json
index bdd01e2..e4cd803 100644
--- a/recipes/engine/femu_test.expected/start_femu.json
+++ b/recipes/engine/femu_test.expected/start_femu.json
@@ -1151,7 +1151,7 @@
"--json-output",
"/path/to/tmp/json",
"copy",
- "RECIPE_MODULE[fuchsia::gsutil]/resources/tool_manifest.json",
+ "RECIPE_MODULE[flutter::gsutil]/resources/tool_manifest.json",
"/path/to/tmp/json"
],
"cwd": "[CACHE]/builder",