Signer recipe downloads artifact, codesigns file, and upload artifact back. Editions include adding kms secrets, tmp directory tracking, unlock keychain in the same session

12/13 led run:
https://ci.chromium.org/raw/build/logs.chromium.org/flutter/led/xilaizhang_google.com/dd35e456014e3ea3360ba3ffadd9dc565abc8213d7076dd45c1774e49c0a87eb/+/build.proto?server=chromium-swarm.appspot.com

12/12 led run: https://ci.chromium.org/raw/build/logs.chromium.org/flutter/led/xilaizhang_google.com/2be4003ab0497daf0e90f84cd6e74a2cdaaa00cf1b6783c9e759386bfdb93afb/+/build.proto?server=chromium-swarm.appspot.com

12/02 led run: https://luci-milo.appspot.com/raw/build/logs.chromium.org/flutter/led/xilaizhang_google.com/8d785d3c27550b0524c00d5ae1ed278721b587ce2c3b7528b952d1889c43bc1a/+/build.proto

Change-Id: I55eeae2bec1ca765006d9d4d2b606299ff5c7e7a
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/35322
Reviewed-by: Godofredo Contreras <godofredoc@google.com>
Commit-Queue: Xilai Zhang <xilaizhang@google.com>
diff --git a/recipe_modules/flutter_deps/api.py b/recipe_modules/flutter_deps/api.py
index 7ed10df..4ffd3d2 100644
--- a/recipe_modules/flutter_deps/api.py
+++ b/recipe_modules/flutter_deps/api.py
@@ -417,11 +417,8 @@
       env(dict): Current environment variables.
       env_prefixes(dict): Current environment prefixes variables.
     """
-    if version != 'latest':
-        msg = 'codesign version is None.'
-        raise ValueError(msg)
     version = version or 'latest'
-    codesign_path = self.m.path.mkdtemp().join('codesign')
+    codesign_path = self.m.path.mkdtemp()
     codesign = self.m.cipd.EnsureFile()
     codesign.add_package('flutter/codesign/${platform}', version)
     with self.m.step.nest('Installing Mac codesign CIPD pkg'):
@@ -429,6 +426,7 @@
     paths = env_prefixes.get('PATH', [])
     paths.append(codesign_path)
     env_prefixes['PATH'] = paths
+    return codesign_path.join('codesign')
 
   def cosign(self, env, env_prefixes, version=None):
     """Installs cosign.
diff --git a/recipe_modules/flutter_deps/examples/full.expected/basic.json b/recipe_modules/flutter_deps/examples/full.expected/basic.json
index 54c4ba7..fd5dfcd 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/basic.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/basic.json
@@ -414,7 +414,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -760,7 +760,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json b/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json
index 5b33f44..394e273 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json
@@ -498,7 +498,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -846,7 +846,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json b/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json
index 54c4ba7..fd5dfcd 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json
@@ -414,7 +414,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -760,7 +760,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/linux.json b/recipe_modules/flutter_deps/examples/full.expected/linux.json
index 54c4ba7..fd5dfcd 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/linux.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/linux.json
@@ -414,7 +414,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -760,7 +760,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json b/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json
index c3d4e15..c21da3c 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json
@@ -498,7 +498,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -846,7 +846,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/mac.json b/recipe_modules/flutter_deps/examples/full.expected/mac.json
index 3a19982..61a1746 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/mac.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/mac.json
@@ -476,7 +476,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/windows.json b/recipe_modules/flutter_deps/examples/full.expected/windows.json
index 4f7e5a0..5fc963f 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/windows.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/windows.json
@@ -414,7 +414,7 @@
       "cipd.bat",
       "ensure",
       "-root",
-      "[CLEANUP]\\tmp_tmp_1\\codesign",
+      "[CLEANUP]\\tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -725,7 +725,7 @@
         "[CLEANUP]\\go_path\\bin",
         "[START_DIR]\\firebase",
         "[CACHE]\\cmake\\bin",
-        "[CLEANUP]\\tmp_tmp_1\\codesign",
+        "[CLEANUP]\\tmp_tmp_1",
         "[CACHE]\\cosign\\bin",
         "[CACHE]\\ninja",
         "[CACHE]\\clang\\bin",
@@ -801,7 +801,7 @@
         "[CLEANUP]\\go_path\\bin",
         "[START_DIR]\\firebase",
         "[CACHE]\\cmake\\bin",
-        "[CLEANUP]\\tmp_tmp_1\\codesign",
+        "[CLEANUP]\\tmp_tmp_1",
         "[CACHE]\\cosign\\bin",
         "[CACHE]\\ninja",
         "[CACHE]\\clang\\bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json b/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json
index 25c37cd..782134b 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json
@@ -414,7 +414,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -760,7 +760,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.expected/with-gems.json b/recipe_modules/flutter_deps/examples/full.expected/with-gems.json
index e409a1d..d9c7054 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/with-gems.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/with-gems.json
@@ -414,7 +414,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -760,7 +760,7 @@
         "[CLEANUP]/go_path/bin",
         "[START_DIR]/firebase",
         "[CACHE]/cmake/bin",
-        "[CLEANUP]/tmp_tmp_1/codesign",
+        "[CLEANUP]/tmp_tmp_1",
         "[CACHE]/cosign/bin",
         "[CACHE]/ninja",
         "[CACHE]/clang/bin",
diff --git a/recipe_modules/flutter_deps/examples/full.py b/recipe_modules/flutter_deps/examples/full.py
index a6d85b3..12f3bf7 100644
--- a/recipe_modules/flutter_deps/examples/full.py
+++ b/recipe_modules/flutter_deps/examples/full.py
@@ -55,8 +55,6 @@
   api.flutter_deps.flutter_engine(env, env_prefixes)
   api.flutter_deps.firebase(env, env_prefixes)
   api.flutter_deps.cmake(env, env_prefixes)
-  with api.assertions.assertRaises(ValueError):
-    api.flutter_deps.codesign(env, env_prefixes)
   api.flutter_deps.codesign(env, env_prefixes, 'latest')
   api.flutter_deps.cosign(env, env_prefixes)
   api.flutter_deps.ninja(env, env_prefixes)
diff --git a/recipes/engine_v2/signer.expected/config_from_file.json b/recipes/engine_v2/signer.expected/config_from_file.json
index 8de884f..f4441a2 100644
--- a/recipes/engine_v2/signer.expected/config_from_file.json
+++ b/recipes/engine_v2/signer.expected/config_from_file.json
@@ -15,7 +15,7 @@
       "cipd",
       "ensure",
       "-root",
-      "[CLEANUP]/tmp_tmp_1/codesign",
+      "[CLEANUP]/tmp_tmp_1",
       "-ensure-file",
       "flutter/codesign/${platform} latest",
       "-max-threads",
@@ -41,20 +41,282 @@
   },
   {
     "cmd": [
-      "codesign",
-      "--gcs-download-path",
-      "gs://a/b/c/artifact.zip",
-      "--gcs-upload-path",
-      "gs://a/b/c/artifact.zip"
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cloudkms",
+      "-ensure-file",
+      "infra/tools/luci/cloudkms/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "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-latest----------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/cloudkms/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": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://flutter_configs/codesign_team_id.encrypted",
+      "[CLEANUP]/codesign_team_id.encrypted"
+    ],
+    "infra_step": true,
+    "name": "gsutil download"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cloudkms/cloudkms",
+      "decrypt",
+      "-input",
+      "[CLEANUP]/codesign_team_id.encrypted",
+      "-output",
+      "[CLEANUP]/tmp_tmp_2/codesign_team_id.encrypted",
+      "projects/flutter-infra-staging/locations/global/keyRings/luci/cryptoKeys/flutter-infra"
+    ],
+    "name": "cloudkms get key"
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cloudkms",
+      "-ensure-file",
+      "infra/tools/luci/cloudkms/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (2)",
+    "~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-latest----------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/cloudkms/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": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://flutter_configs/codesign_app_specific_password.encrypted",
+      "[CLEANUP]/codesign_app_specific_password.encrypted"
+    ],
+    "infra_step": true,
+    "name": "gsutil download (2)"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cloudkms/cloudkms",
+      "decrypt",
+      "-input",
+      "[CLEANUP]/codesign_app_specific_password.encrypted",
+      "-output",
+      "[CLEANUP]/tmp_tmp_2/codesign_app_specific_password.encrypted",
+      "projects/flutter-infra-staging/locations/global/keyRings/luci/cryptoKeys/flutter-infra"
+    ],
+    "name": "cloudkms get key (2)"
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cloudkms",
+      "-ensure-file",
+      "infra/tools/luci/cloudkms/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (3)",
+    "~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-latest----------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/cloudkms/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": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://flutter_configs/codesign_app_store_id.encrypted",
+      "[CLEANUP]/codesign_app_store_id.encrypted"
+    ],
+    "infra_step": true,
+    "name": "gsutil download (3)"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cloudkms/cloudkms",
+      "decrypt",
+      "-input",
+      "[CLEANUP]/codesign_app_store_id.encrypted",
+      "-output",
+      "[CLEANUP]/tmp_tmp_2/codesign_app_store_id.encrypted",
+      "projects/flutter-infra-staging/locations/global/keyRings/luci/cryptoKeys/flutter-infra"
+    ],
+    "name": "cloudkms get key (3)"
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "RECIPE[flutter::engine_v2/signer].resources/runner.sh"
     ],
     "env_prefixes": {
       "PATH": [
-        "[CLEANUP]/tmp_tmp_1/codesign"
+        "[CLEANUP]/tmp_tmp_1"
+      ]
+    },
+    "name": "Set execute permission"
+  },
+  {
+    "cmd": [
+      "bash",
+      "RECIPE[flutter::engine_v2/signer].resources/runner.sh"
+    ],
+    "env_prefixes": {
+      "PATH": [
+        "[CLEANUP]/tmp_tmp_1"
+      ]
+    },
+    "name": "unlock keychain"
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://a/b/c/artifact.zip",
+      "[CLEANUP]/tmp_tmp_3/unsigned_artifact.zip"
+    ],
+    "infra_step": true,
+    "name": "gsutil download gs://a/b/c/artifact.zip"
+  },
+  {
+    "cmd": [
+      "[CLEANUP]/tmp_tmp_1/codesign",
+      "--codesign-cert-name",
+      "FLUTTER.IO LLC",
+      "--no-dryrun",
+      "--app-specific-password-file-path",
+      "[CLEANUP]/tmp_tmp_2/codesign_app_specific_password.encrypted",
+      "--codesign-appstore-id-file-path",
+      "[CLEANUP]/tmp_tmp_2/codesign_app_store_id.encrypted",
+      "--codesign-team-id-file-path",
+      "[CLEANUP]/tmp_tmp_2/codesign_team_id.encrypted",
+      "--input-zip-file-path",
+      "[CLEANUP]/tmp_tmp_3/unsigned_artifact.zip",
+      "--output-zip-file-path",
+      "[CLEANUP]/tmp_tmp_3/artifact.zip"
+    ],
+    "env_prefixes": {
+      "PATH": [
+        "[CLEANUP]/tmp_tmp_1"
       ]
     },
     "name": "codesign Apple engine binaries"
   },
   {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[CLEANUP]/tmp_tmp_4/b/c"
+    ],
+    "infra_step": true,
+    "name": "Ensure b/c"
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[CLEANUP]/tmp_tmp_3/artifact.zip",
+      "[CLEANUP]/tmp_tmp_4/b/c"
+    ],
+    "infra_step": true,
+    "name": "Copy gs://a/b/c/artifact.zip"
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-r",
+      "[CLEANUP]/tmp_tmp_4/*",
+      "gs://a/"
+    ],
+    "infra_step": true,
+    "name": "gsutil b/c/artifact.zip",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://console.cloud.google.com/storage/browser/a/@@@"
+    ]
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipes/engine_v2/signer.py b/recipes/engine_v2/signer.py
index f804fd4..a9740cd 100644
--- a/recipes/engine_v2/signer.py
+++ b/recipes/engine_v2/signer.py
@@ -2,15 +2,34 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# Recipe that executes apple code signing on a code signing bot. A code
+# signing bot is a machine with flutter certificates and signing related
+# set ups.
+#
+# This recipe receives as properties the list of google cloud bucket paths
+# of engine artifacts, and reads code sign related passwords from
+# kms securely. The engine artifact bucket paths and codesign credentials
+# are then supplied to a codesign standalone app, which communicates with
+# Apple notary server to finish code signing. The codesign standalone app
+# is run as a cipd package, and the codesigned artifacts are uploaded back
+# to the same google cloud bucket path.
+
 DEPS = [
+    'depot_tools/gsutil',
+    'flutter/archives',
     'flutter/flutter_deps',
     'recipe_engine/context',
     'recipe_engine/futures',
+    'flutter/kms',
+    'flutter/osx_sdk',
+    'recipe_engine/file',
+    'recipe_engine/path',
     'recipe_engine/platform',
     'recipe_engine/properties',
     'recipe_engine/step',
 ]
 
+
 def RunSteps(api):
   if not api.platform.is_mac:
     pass
@@ -19,32 +38,100 @@
   env = {}
   env_prefixes = {}
   with api.step.nest('Dependencies'):
-    codesign_deps = api.properties.get('dependencies')
-    api.flutter_deps.required_deps(env, env_prefixes, codesign_deps)
+    codesign_path = api.flutter_deps.codesign(env, env_prefixes)
 
   # The list is iterated running one signer tool command per file. This can be
   # optimized using the multiprocessing API.
-  final_list = api.properties.get('signing_file_list', [])
+  final_sources_list = api.properties.get('signing_file_list', [])
   signer_builds = []
-  for gcsPath in final_list:
-    signer_builds.append(
-        api.futures.spawn(RunSignerToolCommand, api, env, env_prefixes, gcsPath, gcsPath)
-    )
-  futures = api.futures.wait(signer_builds)
-  for future in futures:
-    future.result()
+  codesign_dir = api.path.mkdtemp()
+  app_specific_password_filepath = codesign_dir.join(
+      'codesign_app_specific_password.encrypted'
+  )
+  appstore_id_filepath = codesign_dir.join('codesign_app_store_id.encrypted')
+  team_id_filepath = codesign_dir.join('codesign_team_id.encrypted')
+  api.kms.get_secret('codesign_team_id.encrypted', team_id_filepath)
+  api.kms.get_secret(
+      'codesign_app_specific_password.encrypted', app_specific_password_filepath
+  )
+  api.kms.get_secret('codesign_app_store_id.encrypted', appstore_id_filepath)
 
+  # unlock keychain
+  with api.context(env=env, env_prefixes=env_prefixes):
+    resource_name = api.resource('runner.sh')
+    api.step('Set execute permission', ['chmod', '755', resource_name])
+    cmd = ['bash', resource_name]
+    api.step('unlock keychain', cmd)
 
-def RunSignerToolCommand(api, env, env_prefixes, gcsDownloadPath, gcsUploadPath):
+  # keep track of the output zip files in separate temp folders to avoid name conflicts
+  output_zips = {}
+
+  codesign_string_path = "%s" % codesign_path
+  with api.osx_sdk('ios'):
+    for source_path in final_sources_list:
+      input_tmp_folder = api.path.mkdtemp()
+      _, artifact_base_name = api.path.split(source_path) 
+      local_zip_path = input_tmp_folder.join('unsigned_%s' % artifact_base_name)
+      local_zip_string_path = str(local_zip_path)
+
+      output_zip_path = input_tmp_folder.join(artifact_base_name)
+      output_zip_string_path = str(output_zip_path)
+      output_zips[source_path] = output_zip_string_path
+      api.archives.download(source_path, local_zip_path)
+      signer_builds.append(
+          api.futures.spawn(
+              RunSignerToolCommand, api, env, env_prefixes,
+              local_zip_string_path, output_zip_string_path,
+              app_specific_password_filepath, appstore_id_filepath,
+              team_id_filepath, codesign_string_path
+          )
+      )
+
+    futures = api.futures.wait(signer_builds)
+    for future in futures:
+      future.result()
+
+  for source_path, output_zip_path in output_zips.items():
+    api.archives.upload_artifact(src=output_zip_path, dst=source_path)
+
+def RunSignerToolCommand(
+    api, env, env_prefixes, input_zip_string_path, output_zip_string_path,
+    app_specific_password_filepath, appstore_id_filepath, team_id_filepath,
+    codesign_string_path
+):
+  """Runs code sign standalone app.
+
+  Args:
+      input_zip_string_path (str): path of the unsigned artifact in the file system.
+      output_zip_string_path (str): path of the signed artifact in the file system.
+      app_specific_password_filepath (str) : path of app specific password, one of
+      the code sign credentials.
+      appstore_id_filepath (str) : path of apple store id, one of the codesign
+      credentials.
+      team_id_filepath (str) : path of flutter team id used for codesign, one of the
+      codesign credentials.
+      codesign_string_path (str): the absolute path of the codesign standalone app
+      cipd package. This is to differentiate codesign cipd from mac system codesign.
+  """
+  flutter_certificate_name = 'FLUTTER.IO LLC'
   with api.context(env=env, env_prefixes=env_prefixes):
     api.step(
         'codesign Apple engine binaries',
         [
-          'codesign',
-          '--gcs-download-path',
-          gcsDownloadPath,
-          '--gcs-upload-path',
-          gcsUploadPath,
+            codesign_string_path,
+            '--codesign-cert-name',
+            flutter_certificate_name,
+            '--no-dryrun',
+            '--app-specific-password-file-path',
+            app_specific_password_filepath,
+            '--codesign-appstore-id-file-path',
+            appstore_id_filepath,
+            '--codesign-team-id-file-path',
+            team_id_filepath,
+            '--input-zip-file-path',
+            input_zip_string_path,
+            '--output-zip-file-path',
+            output_zip_string_path,
         ],
     )
 
@@ -58,7 +145,6 @@
               'dependency': 'codesign',
               'version': 'latest',
           }],
-          signing_file_list = ["gs://a/b/c/artifact.zip"]
+          signing_file_list=["gs://a/b/c/artifact.zip"]
       )
   )
-
diff --git a/recipes/engine_v2/signer.resources/runner.sh b/recipes/engine_v2/signer.resources/runner.sh
new file mode 100644
index 0000000..a35286d
--- /dev/null
+++ b/recipes/engine_v2/signer.resources/runner.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# Helper script to unlock the keychain in the same session
+# as the test runner script.
+set -e
+
+if [ -f /usr/local/bin/unlock_login_keychain.sh ]
+then
+  /usr/local/bin/unlock_login_keychain.sh
+else
+  echo "This bot does not support codesigning"
+fi
\ No newline at end of file