From c533a0a4cfc4962971818edcfac47a2899e69799 Mon Sep 17 00:00:00 2001
From: Finley Garton <35561611+finleygn@users.noreply.github.com>
Date: Fri, 22 Sep 2023 18:30:36 +0100
Subject: [PATCH] Add support for partial checkout filters (#1396)

* added filter option & tests

* added build file

* fix test oversight

* added exit 1

* updated docs to specify override

* undo unneeded readme change

* set to undefined rather than empty string

* run git config in correct di

---------

Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com>
---
 .github/workflows/test.yml       | 10 ++++++++++
 README.md                        |  6 +++++-
 __test__/git-auth-helper.test.ts |  1 +
 __test__/input-helper.test.ts    |  1 +
 __test__/verify-fetch-filter.sh  | 16 ++++++++++++++++
 action.yml                       |  7 ++++++-
 dist/index.js                    | 12 +++++++++++-
 src/git-source-provider.ts       |  8 +++++++-
 src/git-source-settings.ts       |  5 +++++
 src/input-helper.ts              |  8 ++++++++
 10 files changed, 70 insertions(+), 4 deletions(-)
 create mode 100755 __test__/verify-fetch-filter.sh

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 68c7118..15996ee 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -72,6 +72,16 @@ jobs:
         shell: bash
         run: __test__/verify-side-by-side.sh
 
+      # Filter
+      - name: Fetch filter
+        uses: ./
+        with:
+          filter: 'blob:none'
+          path: fetch-filter
+
+      - name: Verify fetch filter
+        run: __test__/verify-fetch-filter.sh
+
       # Sparse checkout
       - name: Sparse checkout
         uses: ./
diff --git a/README.md b/README.md
index 7e76cde..92bc784 100644
--- a/README.md
+++ b/README.md
@@ -75,8 +75,12 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
     # Default: true
     clean: ''
 
+    # Partially clone against a given filter. Overrides sparse-checkout if set.
+    # Default: null
+    filter: ''
+
     # Do a sparse checkout on given patterns. Each pattern should be separated with
-    # new lines
+    # new lines.
     # Default: null
     sparse-checkout: ''
 
diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts
index a0cff63..411faed 100644
--- a/__test__/git-auth-helper.test.ts
+++ b/__test__/git-auth-helper.test.ts
@@ -802,6 +802,7 @@ async function setup(testName: string): Promise<void> {
     authToken: 'some auth token',
     clean: true,
     commit: '',
+    filter: undefined,
     sparseCheckout: [],
     sparseCheckoutConeMode: true,
     fetchDepth: 1,
diff --git a/__test__/input-helper.test.ts b/__test__/input-helper.test.ts
index 21932ca..9514cb4 100644
--- a/__test__/input-helper.test.ts
+++ b/__test__/input-helper.test.ts
@@ -79,6 +79,7 @@ describe('input-helper tests', () => {
     expect(settings.clean).toBe(true)
     expect(settings.commit).toBeTruthy()
     expect(settings.commit).toBe('1234567890123456789012345678901234567890')
+    expect(settings.filter).toBe(undefined)
     expect(settings.sparseCheckout).toBe(undefined)
     expect(settings.sparseCheckoutConeMode).toBe(true)
     expect(settings.fetchDepth).toBe(1)
diff --git a/__test__/verify-fetch-filter.sh b/__test__/verify-fetch-filter.sh
new file mode 100755
index 0000000..4fc9d9e
--- /dev/null
+++ b/__test__/verify-fetch-filter.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Verify .git folder
+if [ ! -d "./fetch-filter/.git" ]; then
+  echo "Expected ./fetch-filter/.git folder to exist"
+  exit 1
+fi
+
+# Verify .git/config contains partialclonefilter
+
+CLONE_FILTER=$(git -C fetch-filter config --local --get remote.origin.partialclonefilter)
+
+if [ "$CLONE_FILTER" != "blob:none" ]; then
+  echo "Expected ./fetch-filter/.git/config to have 'remote.origin.partialclonefilter' set to 'blob:none'"
+  exit 1
+fi
diff --git a/action.yml b/action.yml
index 43d408d..5aa90a7 100644
--- a/action.yml
+++ b/action.yml
@@ -53,10 +53,15 @@ inputs:
   clean:
     description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
     default: true
+  filter:
+    description: >
+      Partially clone against a given filter.
+      Overrides sparse-checkout if set.
+    default: null
   sparse-checkout:
     description: >
       Do a sparse checkout on given patterns.
-      Each pattern should be separated with new lines
+      Each pattern should be separated with new lines.
     default: null
   sparse-checkout-cone-mode:
     description: >
diff --git a/dist/index.js b/dist/index.js
index 67752ae..ddf2b3d 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1244,8 +1244,12 @@ function getSource(settings) {
             // Fetch
             core.startGroup('Fetching the repository');
             const fetchOptions = {};
-            if (settings.sparseCheckout)
+            if (settings.filter) {
+                fetchOptions.filter = settings.filter;
+            }
+            else if (settings.sparseCheckout) {
                 fetchOptions.filter = 'blob:none';
+            }
             if (settings.fetchDepth <= 0) {
                 // Fetch all branches and tags
                 let refSpec = refHelper.getRefSpecForAllHistory(settings.ref, settings.commit);
@@ -1723,6 +1727,12 @@ function getInputs() {
         // Clean
         result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE';
         core.debug(`clean = ${result.clean}`);
+        // Filter
+        const filter = core.getInput('filter');
+        if (filter) {
+            result.filter = filter;
+        }
+        core.debug(`filter = ${result.filter}`);
         // Sparse checkout
         const sparseCheckout = core.getMultilineInput('sparse-checkout');
         if (sparseCheckout.length) {
diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts
index c1360b8..5c98e9f 100644
--- a/src/git-source-provider.ts
+++ b/src/git-source-provider.ts
@@ -159,7 +159,13 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
       fetchTags?: boolean
       showProgress?: boolean
     } = {}
-    if (settings.sparseCheckout) fetchOptions.filter = 'blob:none'
+
+    if (settings.filter) {
+      fetchOptions.filter = settings.filter
+    } else if (settings.sparseCheckout) {
+      fetchOptions.filter = 'blob:none'
+    }
+
     if (settings.fetchDepth <= 0) {
       // Fetch all branches and tags
       let refSpec = refHelper.getRefSpecForAllHistory(
diff --git a/src/git-source-settings.ts b/src/git-source-settings.ts
index 2f80b5e..629350b 100644
--- a/src/git-source-settings.ts
+++ b/src/git-source-settings.ts
@@ -29,6 +29,11 @@ export interface IGitSourceSettings {
    */
   clean: boolean
 
+  /**
+   * The filter determining which objects to include
+   */
+  filter: string | undefined
+
   /**
    * The array of folders to make the sparse checkout
    */
diff --git a/src/input-helper.ts b/src/input-helper.ts
index be9cecd..e546c19 100644
--- a/src/input-helper.ts
+++ b/src/input-helper.ts
@@ -82,6 +82,14 @@ export async function getInputs(): Promise<IGitSourceSettings> {
   result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE'
   core.debug(`clean = ${result.clean}`)
 
+  // Filter
+  const filter = core.getInput('filter')
+  if (filter) {
+    result.filter = filter
+  }
+
+  core.debug(`filter = ${result.filter}`)
+
   // Sparse checkout
   const sparseCheckout = core.getMultilineInput('sparse-checkout')
   if (sparseCheckout.length) {