From aadec899646c8e0f34c52d9219c2faac36626b55 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Wed, 21 Feb 2024 13:56:19 +0100
Subject: [PATCH] Explicitly disable sparse checkout unless asked for (#1598)

When a worktree is reused by actions/checkout and the first time sparse checkout was enabled, we need to ensure that the second time it is only a sparse checkout if explicitly asked for. Otherwise, we need to disable the sparse checkout so that a full checkout is the outcome of this Action.

## Details
* If no `sparse-checkout` parameter is specified, disable it

This should allow users to reuse existing folders when running
`actions/checkout` where a previous run asked for a sparse checkout but
the current run does not ask for a sparse checkout.

This fixes https://github.com/actions/checkout/issues/1475

There are use cases in particular with non-ephemeral (self-hosted) runners where an
existing worktree (that has been initialized as a sparse checkout) is
reused in subsequent CI runs (where `actions/checkout` is run _without_
any `sparse-checkout` parameter).

In these scenarios, we need to make sure that the sparse checkout is
disabled before checking out the files.

### Also includes:

* npm run build
* ci: verify that an existing sparse checkout can be made unsparse
* Added a clarifying comment about test branches.
* `test-proxy` now uses newly-minted `test-ubuntu-git` container image from ghcr.io

---------

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Co-authored-by: John Wesley Walker III <81404201+jww3@users.noreply.github.com>
---
 .github/workflows/test.yml            | 19 +++++++++++++++++--
 __test__/git-auth-helper.test.ts      |  1 +
 __test__/git-directory-helper.test.ts |  1 +
 dist/index.js                         | 10 +++++++++-
 src/git-command-manager.ts            |  5 +++++
 src/git-source-provider.ts            |  4 +++-
 6 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 15996ee..d8c83ac 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,6 +7,11 @@ on:
       - main
       - releases/*
 
+
+# Note that when you see patterns like "ref: test-data/v2/basic" within this workflow, 
+# these refer to "test-data" branches on this actions/checkout repo.
+# (For example, test-data/v2/basic -> https://github.com/actions/checkout/tree/test-data/v2/basic)
+
 jobs:
   build:
     runs-on: ubuntu-latest
@@ -95,6 +100,16 @@ jobs:
       - name: Verify sparse checkout
         run: __test__/verify-sparse-checkout.sh
 
+      # Disabled sparse checkout in existing checkout
+      - name: Disabled sparse checkout
+        uses: ./
+        with:
+          path: sparse-checkout
+
+      - name: Verify disabled sparse checkout
+        shell: bash
+        run: set -x && ls -l sparse-checkout/src/git-command-manager.ts
+
       # Sparse checkout (non-cone mode)
       - name: Sparse checkout (non-cone mode)
         uses: ./
@@ -175,7 +190,7 @@ jobs:
   test-proxy:
     runs-on: ubuntu-latest
     container:
-      image: alpine/git:latest
+      image: ghcr.io/actions/test-ubuntu-git:main.20240221.114913.703z
       options: --dns 127.0.0.1
     services:
       squid-proxy:
@@ -279,4 +294,4 @@ jobs:
       - name: Fix Checkout v3
         uses: actions/checkout@v3
         with:
-          path: v3
\ No newline at end of file
+          path: v3
diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts
index 411faed..a75b79d 100644
--- a/__test__/git-auth-helper.test.ts
+++ b/__test__/git-auth-helper.test.ts
@@ -727,6 +727,7 @@ async function setup(testName: string): Promise<void> {
     branchDelete: jest.fn(),
     branchExists: jest.fn(),
     branchList: jest.fn(),
+    disableSparseCheckout: jest.fn(),
     sparseCheckout: jest.fn(),
     sparseCheckoutNonConeMode: jest.fn(),
     checkout: jest.fn(),
diff --git a/__test__/git-directory-helper.test.ts b/__test__/git-directory-helper.test.ts
index 362133f..79e0538 100644
--- a/__test__/git-directory-helper.test.ts
+++ b/__test__/git-directory-helper.test.ts
@@ -462,6 +462,7 @@ async function setup(testName: string): Promise<void> {
     branchList: jest.fn(async () => {
       return []
     }),
+    disableSparseCheckout: jest.fn(),
     sparseCheckout: jest.fn(),
     sparseCheckoutNonConeMode: jest.fn(),
     checkout: jest.fn(),
diff --git a/dist/index.js b/dist/index.js
index ddf2b3d..1389602 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -576,6 +576,11 @@ class GitCommandManager {
             return result;
         });
     }
+    disableSparseCheckout() {
+        return __awaiter(this, void 0, void 0, function* () {
+            yield this.execGit(['sparse-checkout', 'disable']);
+        });
+    }
     sparseCheckout(sparseCheckout) {
         return __awaiter(this, void 0, void 0, function* () {
             yield this.execGit(['sparse-checkout', 'set', ...sparseCheckout]);
@@ -1282,7 +1287,10 @@ function getSource(settings) {
                 core.endGroup();
             }
             // Sparse checkout
-            if (settings.sparseCheckout) {
+            if (!settings.sparseCheckout) {
+                yield git.disableSparseCheckout();
+            }
+            else {
                 core.startGroup('Setting up sparse checkout');
                 if (settings.sparseCheckoutConeMode) {
                     yield git.sparseCheckout(settings.sparseCheckout);
diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts
index 7752cfa..0f3fd25 100644
--- a/src/git-command-manager.ts
+++ b/src/git-command-manager.ts
@@ -17,6 +17,7 @@ export interface IGitCommandManager {
   branchDelete(remote: boolean, branch: string): Promise<void>
   branchExists(remote: boolean, pattern: string): Promise<boolean>
   branchList(remote: boolean): Promise<string[]>
+  disableSparseCheckout(): Promise<void>
   sparseCheckout(sparseCheckout: string[]): Promise<void>
   sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void>
   checkout(ref: string, startPoint: string): Promise<void>
@@ -171,6 +172,10 @@ class GitCommandManager {
     return result
   }
 
+  async disableSparseCheckout(): Promise<void> {
+    await this.execGit(['sparse-checkout', 'disable'])
+  }
+
   async sparseCheckout(sparseCheckout: string[]): Promise<void> {
     await this.execGit(['sparse-checkout', 'set', ...sparseCheckout])
   }
diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts
index 5c98e9f..0589722 100644
--- a/src/git-source-provider.ts
+++ b/src/git-source-provider.ts
@@ -208,7 +208,9 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
     }
 
     // Sparse checkout
-    if (settings.sparseCheckout) {
+    if (!settings.sparseCheckout) {
+      await git.disableSparseCheckout()
+    } else {
       core.startGroup('Setting up sparse checkout')
       if (settings.sparseCheckoutConeMode) {
         await git.sparseCheckout(settings.sparseCheckout)