git_archive: Initial commit

Bug: b/346371820
Change-Id: I3420c8a509452057db6bcc19c245b82563347eac
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/215216
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Reviewed-by: Taylor Cramer <cramertj@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed-service-accounts.iam.gserviceaccount.com>
diff --git a/recipes/git_archive.proto b/recipes/git_archive.proto
new file mode 100644
index 0000000..1b587f3
--- /dev/null
+++ b/recipes/git_archive.proto
@@ -0,0 +1,27 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+syntax = "proto3";
+
+import "recipe_modules/pigweed/checkout/options.proto";
+
+package recipes.pigweed.git_archive;
+
+message InputProperties {
+  // Checkout module options.
+  recipe_modules.pigweed.checkout.Options checkout_options = 1;
+
+  // Archive destination.
+  string gcs_bucket = 2;
+}
diff --git a/recipes/git_archive.py b/recipes/git_archive.py
new file mode 100644
index 0000000..5131611
--- /dev/null
+++ b/recipes/git_archive.py
@@ -0,0 +1,82 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Recipe for testing Pigweed using presubmit_checks.py script."""
+
+from typing import Generator
+
+from PB.recipes.pigweed.git_archive import InputProperties
+
+from recipe_engine import post_process, recipe_api, recipe_test_api
+
+DEPS = [
+    'fuchsia/buildbucket_util',
+    'fuchsia/git',
+    'fuchsia/gsutil',
+    'pigweed/checkout',
+    'recipe_engine/path',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+PROPERTIES = InputProperties
+
+
+def RunSteps(
+    api: recipe_api.RecipeScriptApi,
+    props: InputProperties,
+) -> None:
+    """Run Pigweed presubmit checks."""
+    if api.buildbucket_util.is_tryjob:
+        raise api.step.InfraFailure('This recipe does not support tryjobs')
+
+    checkout = api.checkout(props.checkout_options)
+
+    tar_name = f'{checkout.revision()}.tar.gz'
+    archive = api.path.mkdtemp('archive') / tar_name
+    cmd = [
+        'archive',
+        '--output',
+        archive,
+        '--format=.tar.gz',
+        'HEAD',
+    ]
+    api.git('archive', *cmd)
+
+    assert props.gcs_bucket
+    api.gsutil.upload(bucket=props.gcs_bucket, src=archive, dst=tar_name)
+
+
+def GenTests(api) -> Generator[recipe_test_api.TestData, None, None]:
+    """Create tests."""
+
+    yield api.test(
+        'ci',
+        api.properties(
+            gcs_bucket='bucket-bucket-bucket',
+            checkout_options=api.checkout.git_options(),
+        ),
+        api.checkout.ci_test_data(),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'try',
+        api.properties(
+            gcs_bucket='bucket-bucket-bucket',
+            checkout_options=api.checkout.git_options(),
+        ),
+        api.checkout.try_test_data(),
+        api.post_process(post_process.DropExpectation),
+        status='INFRA_FAILURE',
+    )