[upload_debug_symbols] Create recipe module

As the upload_debug_symbols tool is soon going to support more
options, it's nice to have the logic moved out of build/api.py
and into its own recipe module.

Change-Id: I828ca8c57be9955c42d9c3d7d532cfbfd94fe35c
diff --git a/recipe_modules/build/__init__.py b/recipe_modules/build/__init__.py
index 937c26e..959fe18 100644
--- a/recipe_modules/build/__init__.py
+++ b/recipe_modules/build/__init__.py
@@ -13,6 +13,7 @@
     'infra/tar',
     'infra/testsharder',
     'infra/upload',
+    'infra/upload_debug_symbols',
     'infra/zbi',
     'recipe_engine/cipd',
     'recipe_engine/buildbucket',
diff --git a/recipe_modules/build/api.py b/recipe_modules/build/api.py
index 8754978..8384fef 100644
--- a/recipe_modules/build/api.py
+++ b/recipe_modules/build/api.py
@@ -60,9 +60,6 @@
 CHECKOUT_AUTHORIZED_KEY = '.ssh/authorized_keys'
 CHECKOUT_PRIVATE_KEY = '.ssh/pkey'
 
-# Please use led to test fuchsia recipe runs when updating these pins.
-UPLOAD_DEBUG_SYMBOLS_CIPD_VERSION = 'git_revision:76afdab6dec30f87a8d08291922e7a572033d9bc'
-
 
 class FuchsiaBuildResults(object):
   """Represents a completed build of Fuchsia."""
@@ -1151,18 +1148,6 @@
       self._upload_debug_symbols(build_results)
 
   def _upload_debug_symbols(self, build_results):
-    with self.m.step.nest('ensure upload_debug_symbols'):
-      with self.m.context(infra_steps=True):
-        pkgs = self.m.cipd.EnsureFile()
-        pkgs.add_package('fuchsia/infra/upload_debug_symbols/${platform}',
-                         UPLOAD_DEBUG_SYMBOLS_CIPD_VERSION)
-        cipd_dir = self.m.path['start_dir'].join('cipd', 'upload_debug_symbols')
-        self.m.cipd.ensure(cipd_dir, pkgs)
-        upload_debug_symbols_tool = cipd_dir.join('upload_debug_symbols')
-    cmd = [
-        upload_debug_symbols_tool,
-        '-bucket=' + self.debug_symbol_bucket,
-    ]
     dirs = [
         build_results.fuchsia_build_dir.join('.build-id'),
         build_results.zircon_build_dir.join('.build-id'),
@@ -1170,8 +1155,11 @@
                                     'clang').join('lib', 'debug', '.build-id'),
         build_results.checkout_root.join('prebuilt', 'build_ids'),
     ]
-    cmd.extend(dirs)
-    self.m.step('upload debug symbols', cmd)
+    self.m.upload_debug_symbols(
+        step_name='upload debug symbols',
+        bucket=self.debug_symbol_bucket,
+        build_id_dirs=dirs,
+    )
 
   def _upload_package_snapshot(self, build_results, gcs_bucket, build_id):
     assert gcs_bucket
diff --git a/recipe_modules/build/examples/full.expected/asan.json b/recipe_modules/build/examples/full.expected/asan.json
index 4919f7c..282c8b9 100644
--- a/recipe_modules/build/examples/full.expected/asan.json
+++ b/recipe_modules/build/examples/full.expected/asan.json
@@ -1434,7 +1434,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/blobstats_fails.json b/recipe_modules/build/examples/full.expected/blobstats_fails.json
index 36a7e63..160b1e7 100644
--- a/recipe_modules/build/examples/full.expected/blobstats_fails.json
+++ b/recipe_modules/build/examples/full.expected/blobstats_fails.json
@@ -1298,7 +1298,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/board_with_packages_builder_tester_api.json b/recipe_modules/build/examples/full.expected/board_with_packages_builder_tester_api.json
index c5a5f52..663a9eb 100644
--- a/recipe_modules/build/examples/full.expected/board_with_packages_builder_tester_api.json
+++ b/recipe_modules/build/examples/full.expected/board_with_packages_builder_tester_api.json
@@ -1431,7 +1431,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/clang_toolchain_from_cipd.json b/recipe_modules/build/examples/full.expected/clang_toolchain_from_cipd.json
index ed6780d..957173d 100644
--- a/recipe_modules/build/examples/full.expected/clang_toolchain_from_cipd.json
+++ b/recipe_modules/build/examples/full.expected/clang_toolchain_from_cipd.json
@@ -1098,7 +1098,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/clang_toolchain_from_isolate.json b/recipe_modules/build/examples/full.expected/clang_toolchain_from_isolate.json
index 01592cc..2e40347 100644
--- a/recipe_modules/build/examples/full.expected/clang_toolchain_from_isolate.json
+++ b/recipe_modules/build/examples/full.expected/clang_toolchain_from_isolate.json
@@ -1088,7 +1088,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/compdb.json b/recipe_modules/build/examples/full.expected/compdb.json
index 9729b70..10e16e6 100644
--- a/recipe_modules/build/examples/full.expected/compdb.json
+++ b/recipe_modules/build/examples/full.expected/compdb.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/default.json b/recipe_modules/build/examples/full.expected/default.json
index 9729b70..10e16e6 100644
--- a/recipe_modules/build/examples/full.expected/default.json
+++ b/recipe_modules/build/examples/full.expected/default.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/exclude_images.json b/recipe_modules/build/examples/full.expected/exclude_images.json
index f777ac2..a04a76e 100644
--- a/recipe_modules/build/examples/full.expected/exclude_images.json
+++ b/recipe_modules/build/examples/full.expected/exclude_images.json
@@ -1236,7 +1236,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/extract_build_artifacts.json b/recipe_modules/build/examples/full.expected/extract_build_artifacts.json
index 79e5e0a..6fc9524 100644
--- a/recipe_modules/build/examples/full.expected/extract_build_artifacts.json
+++ b/recipe_modules/build/examples/full.expected/extract_build_artifacts.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/extract_build_artifacts_with_images.json b/recipe_modules/build/examples/full.expected/extract_build_artifacts_with_images.json
index 5d567f0..07db39d 100644
--- a/recipe_modules/build/examples/full.expected/extract_build_artifacts_with_images.json
+++ b/recipe_modules/build/examples/full.expected/extract_build_artifacts_with_images.json
@@ -1389,7 +1389,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/generated_sources.json b/recipe_modules/build/examples/full.expected/generated_sources.json
index 9729b70..10e16e6 100644
--- a/recipe_modules/build/examples/full.expected/generated_sources.json
+++ b/recipe_modules/build/examples/full.expected/generated_sources.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/gn_args.json b/recipe_modules/build/examples/full.expected/gn_args.json
index 08f087f..b6d3ebf 100644
--- a/recipe_modules/build/examples/full.expected/gn_args.json
+++ b/recipe_modules/build/examples/full.expected/gn_args.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/goma_local_cache.json b/recipe_modules/build/examples/full.expected/goma_local_cache.json
index 9729b70..10e16e6 100644
--- a/recipe_modules/build/examples/full.expected/goma_local_cache.json
+++ b/recipe_modules/build/examples/full.expected/goma_local_cache.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/host_asan.json b/recipe_modules/build/examples/full.expected/host_asan.json
index e80e975..72ec433 100644
--- a/recipe_modules/build/examples/full.expected/host_asan.json
+++ b/recipe_modules/build/examples/full.expected/host_asan.json
@@ -1322,7 +1322,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/lto.json b/recipe_modules/build/examples/full.expected/lto.json
index bb5fab5..829aeff 100644
--- a/recipe_modules/build/examples/full.expected/lto.json
+++ b/recipe_modules/build/examples/full.expected/lto.json
@@ -1322,7 +1322,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/mac.json b/recipe_modules/build/examples/full.expected/mac.json
index 234c14b..d5c642a 100644
--- a/recipe_modules/build/examples/full.expected/mac.json
+++ b/recipe_modules/build/examples/full.expected/mac.json
@@ -1453,7 +1453,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/mac-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/ninja_targets.json b/recipe_modules/build/examples/full.expected/ninja_targets.json
index 6d237a1..4a64d61 100644
--- a/recipe_modules/build/examples/full.expected/ninja_targets.json
+++ b/recipe_modules/build/examples/full.expected/ninja_targets.json
@@ -1322,7 +1322,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/product_with_universe_packages.json b/recipe_modules/build/examples/full.expected/product_with_universe_packages.json
index d8105a5..4e5e51e 100644
--- a/recipe_modules/build/examples/full.expected/product_with_universe_packages.json
+++ b/recipe_modules/build/examples/full.expected/product_with_universe_packages.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/release_with_version.json b/recipe_modules/build/examples/full.expected/release_with_version.json
index 4e16bdb..70c1771 100644
--- a/recipe_modules/build/examples/full.expected/release_with_version.json
+++ b/recipe_modules/build/examples/full.expected/release_with_version.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/storage_sparse_too_large.json b/recipe_modules/build/examples/full.expected/storage_sparse_too_large.json
index 2806a33..1c630e9 100644
--- a/recipe_modules/build/examples/full.expected/storage_sparse_too_large.json
+++ b/recipe_modules/build/examples/full.expected/storage_sparse_too_large.json
@@ -1420,7 +1420,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/thinlto.json b/recipe_modules/build/examples/full.expected/thinlto.json
index b48dd42..9631e7d 100644
--- a/recipe_modules/build/examples/full.expected/thinlto.json
+++ b/recipe_modules/build/examples/full.expected/thinlto.json
@@ -1322,7 +1322,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/upload_build_metrics.json b/recipe_modules/build/examples/full.expected/upload_build_metrics.json
index a9651b6..c4e6d72 100644
--- a/recipe_modules/build/examples/full.expected/upload_build_metrics.json
+++ b/recipe_modules/build/examples/full.expected/upload_build_metrics.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_release_version.json b/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_release_version.json
index 1a8ec45..82b7293 100644
--- a/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_release_version.json
+++ b/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_release_version.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_snapshot.json b/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_snapshot.json
index 3a3bba0..2d3f658 100644
--- a/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_snapshot.json
+++ b/recipe_modules/build/examples/full.expected/upload_debug_symbols_from_snapshot.json
@@ -1320,7 +1320,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/upload_filesystem_sizes.json b/recipe_modules/build/examples/full.expected/upload_filesystem_sizes.json
index c1b95fe..de1ce27 100644
--- a/recipe_modules/build/examples/full.expected/upload_filesystem_sizes.json
+++ b/recipe_modules/build/examples/full.expected/upload_filesystem_sizes.json
@@ -1427,7 +1427,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/build/examples/full.expected/zbi_tests.json b/recipe_modules/build/examples/full.expected/zbi_tests.json
index 997c778..aa2324c 100644
--- a/recipe_modules/build/examples/full.expected/zbi_tests.json
+++ b/recipe_modules/build/examples/full.expected/zbi_tests.json
@@ -1315,7 +1315,8 @@
   {
     "cmd": [
       "[START_DIR]/cipd/upload_debug_symbols/upload_debug_symbols",
-      "-bucket=fuchsia-infra-debug-symbols",
+      "-bucket",
+      "fuchsia-infra-debug-symbols",
       "[START_DIR]/out/default/.build-id",
       "[START_DIR]/out/default.zircon/.build-id",
       "[START_DIR]/prebuilt/third_party/clang/linux-x64/lib/debug/.build-id",
diff --git a/recipe_modules/upload_debug_symbols/__init__.py b/recipe_modules/upload_debug_symbols/__init__.py
new file mode 100644
index 0000000..c18d7ea
--- /dev/null
+++ b/recipe_modules/upload_debug_symbols/__init__.py
@@ -0,0 +1,10 @@
+# Copyright 2018 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 = [
+    'recipe_engine/cipd',
+    'recipe_engine/context',
+    'recipe_engine/path',
+    'recipe_engine/step',
+]
diff --git a/recipe_modules/upload_debug_symbols/api.py b/recipe_modules/upload_debug_symbols/api.py
new file mode 100644
index 0000000..2c8efe0
--- /dev/null
+++ b/recipe_modules/upload_debug_symbols/api.py
@@ -0,0 +1,48 @@
+# Copyright 2019 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.
+
+from recipe_engine import recipe_api
+
+UPLOAD_DEBUG_SYMBOLS_VERSION = 'git_revision:76afdab6dec30f87a8d08291922e7a572033d9bc'
+
+
+class UploadDebugSymbolsApi(recipe_api.RecipeApi):
+  """APIs for uploading debug symbols."""
+
+  def __init__(self, *args, **kwargs):
+    super(UploadDebugSymbolsApi, self).__init__(*args, **kwargs)
+    self._upload_debug_symbols_tool = None
+
+  def __call__(self,
+               step_name,
+               bucket,
+               build_id_dirs):
+    """
+    Walk a list of .build-id dirs and upload all found debug symbols to GCS.
+
+    Args:
+      step_name (str): The name of the step produced.
+      bucket (str): GCS bucket to upload symbols to.
+      build_id_dirs (seq<Path>): Paths to .build-id dirs.
+    """
+    self._ensure()
+    step_args = [
+        self._upload_debug_symbols_tool,
+        '-bucket',
+        bucket,
+    ]
+    step_args.extend(build_id_dirs)
+    return self.m.step(step_name, step_args)
+
+  def _ensure(self):
+    """Ensures that the upload debug symbols tool is installed."""
+    if not self._upload_debug_symbols_tool:
+      with self.m.step.nest('ensure upload_debug_symbols'):
+        with self.m.context(infra_steps=True):
+          pkgs = self.m.cipd.EnsureFile()
+          pkgs.add_package('fuchsia/infra/upload_debug_symbols/${platform}',
+                           UPLOAD_DEBUG_SYMBOLS_VERSION)
+          cipd_dir = self.m.path['start_dir'].join('cipd', 'upload_debug_symbols')
+          self.m.cipd.ensure(cipd_dir, pkgs)
+          self._upload_debug_symbols_tool = cipd_dir.join('upload_debug_symbols')
diff --git a/recipe_modules/upload_debug_symbols/examples/full.expected/basic.json b/recipe_modules/upload_debug_symbols/examples/full.expected/basic.json
new file mode 100644
index 0000000..178a7dc
--- /dev/null
+++ b/recipe_modules/upload_debug_symbols/examples/full.expected/basic.json
@@ -0,0 +1,46 @@
+[
+  {
+    "cmd": [],
+    "name": "ensure upload_debug_symbols"
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd/upload_debug_symbols",
+      "-ensure-file",
+      "fuchsia/infra/upload_debug_symbols/${platform} git_revision:76afdab6dec30f87a8d08291922e7a572033d9bc",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "ensure upload_debug_symbols.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@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:76a\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/infra/upload_debug_symbols/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]/cipd/upload_debug_symbols/upload_debug_symbols",
+      "-bucket",
+      "test-bucket",
+      "[START_DIR]/path/to/.build-id"
+    ],
+    "name": "upload debug symbols"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/upload_debug_symbols/examples/full.py b/recipe_modules/upload_debug_symbols/examples/full.py
new file mode 100644
index 0000000..88c3765
--- /dev/null
+++ b/recipe_modules/upload_debug_symbols/examples/full.py
@@ -0,0 +1,22 @@
+# Copyright 2018 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 = [
+    'upload_debug_symbols',
+    'recipe_engine/path',
+]
+
+
+def RunSteps(api):
+  api.upload_debug_symbols(
+      step_name='upload debug symbols',
+      bucket='test-bucket',
+      build_id_dirs=[
+          api.path['start_dir'].join('path/to/.build-id'),
+      ],
+  )
+
+
+def GenTests(api):
+  yield api.test('basic')