Initial implementation of a signer recipe.

This recipe will be used to sign mac artifacts for compliance.

Bug: https://github.com/flutter/flutter/issues/113811

Change-Id: I869ce06dfd0b18fb400af6cca4cdcdf82b8c2d4c
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/35006
Reviewed-by: Godofredo Contreras <godofredoc@google.com>
Reviewed-by: Xilai Zhang <xilaizhang@google.com>
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
diff --git a/recipes/engine_v2/signer.expected/config_from_file.json b/recipes/engine_v2/signer.expected/config_from_file.json
new file mode 100644
index 0000000..8de884f
--- /dev/null
+++ b/recipes/engine_v2/signer.expected/config_from_file.json
@@ -0,0 +1,60 @@
+[
+  {
+    "cmd": [],
+    "name": "Dependencies"
+  },
+  {
+    "cmd": [],
+    "name": "Dependencies.Installing Mac codesign CIPD pkg",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CLEANUP]/tmp_tmp_1/codesign",
+      "-ensure-file",
+      "flutter/codesign/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "Dependencies.Installing Mac codesign CIPD pkg.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-latest----------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"flutter/codesign/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": [
+      "codesign",
+      "--gcs-download-path",
+      "gs://a/b/c/artifact.zip",
+      "--gcs-upload-path",
+      "gs://a/b/c/artifact.zip"
+    ],
+    "env_prefixes": {
+      "PATH": [
+        "[CLEANUP]/tmp_tmp_1/codesign"
+      ]
+    },
+    "name": "codesign Apple engine binaries"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/engine_v2/signer.py b/recipes/engine_v2/signer.py
new file mode 100644
index 0000000..f804fd4
--- /dev/null
+++ b/recipes/engine_v2/signer.py
@@ -0,0 +1,64 @@
+# 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_deps',
+    'recipe_engine/context',
+    'recipe_engine/futures',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+def RunSteps(api):
+  if not api.platform.is_mac:
+    pass
+
+  # Installing dependencies for code sign.
+  env = {}
+  env_prefixes = {}
+  with api.step.nest('Dependencies'):
+    codesign_deps = api.properties.get('dependencies')
+    api.flutter_deps.required_deps(env, env_prefixes, codesign_deps)
+
+  # 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', [])
+  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()
+
+
+def RunSignerToolCommand(api, env, env_prefixes, gcsDownloadPath, gcsUploadPath):
+  with api.context(env=env, env_prefixes=env_prefixes):
+    api.step(
+        'codesign Apple engine binaries',
+        [
+          'codesign',
+          '--gcs-download-path',
+          gcsDownloadPath,
+          '--gcs-upload-path',
+          gcsUploadPath,
+        ],
+    )
+
+
+def GenTests(api):
+
+  yield api.test(
+      'config_from_file',
+      api.properties(
+          dependencies=[{
+              'dependency': 'codesign',
+              'version': 'latest',
+          }],
+          signing_file_list = ["gs://a/b/c/artifact.zip"]
+      )
+  )
+