From 8b5e8b768746b50394015010d25e690bfab9dfbc Mon Sep 17 00:00:00 2001
From: Simon Baird <simon.baird@gmail.com>
Date: Fri, 1 Sep 2023 14:19:18 -0400
Subject: [PATCH] Support fetching without the --progress option (#1067)

Setting the `show-progress` option to false in the `with` section of the
workflow step will cause git fetch to run without `--progress`.

The motivation is to be able to suppress the noisy progress status
output which adds many hundreds of "remote: Counting objects: 85%
(386/453)" and similar lines in the workflow log.

This should be sufficient to resolve #894 and its older friends,
though the solution is different to the one proposed there because
it doesn't use the --quiet flag. IIUC git doesn't show the progress
status by default since the output is not a terminal, so that's why
removing the --progress option is all that's needed.

Adding the --quiet flag doesn't make a lot of difference once the
--progress flag is removed, and actually I think using --quiet would
suppress some other more useful output that would be better left
visible.

Signed-off-by: Simon Baird <sbaird@redhat.com>
---
 README.md                            |   4 +
 __test__/git-auth-helper.test.ts     |   1 +
 __test__/git-command-manager.test.ts | 120 ++++++++++++++++++++++++++-
 __test__/input-helper.test.ts        |   1 +
 action.yml                           |   3 +
 dist/index.js                        |   9 +-
 src/git-command-manager.ts           |  13 ++-
 src/git-source-provider.ts           |   1 +
 src/git-source-settings.ts           |   5 ++
 src/input-helper.ts                  |   5 ++
 10 files changed, 155 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 9df4ae0..982748a 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,10 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
     # Default: false
     fetch-tags: ''
 
+    # Whether to show progress status output when fetching.
+    # Default: true
+    show-progress: ''
+
     # Whether to download Git-LFS files
     # Default: false
     lfs: ''
diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts
index 8e580c1..a0cff63 100644
--- a/__test__/git-auth-helper.test.ts
+++ b/__test__/git-auth-helper.test.ts
@@ -806,6 +806,7 @@ async function setup(testName: string): Promise<void> {
     sparseCheckoutConeMode: true,
     fetchDepth: 1,
     fetchTags: false,
+    showProgress: true,
     lfs: false,
     submodules: false,
     nestedSubmodules: false,
diff --git a/__test__/git-command-manager.test.ts b/__test__/git-command-manager.test.ts
index 16c348f..cea73d4 100644
--- a/__test__/git-command-manager.test.ts
+++ b/__test__/git-command-manager.test.ts
@@ -135,7 +135,6 @@ describe('Test fetchDepth and fetchTags options', () => {
         'protocol.version=2',
         'fetch',
         '--prune',
-        '--progress',
         '--no-recurse-submodules',
         '--filter=filterValue',
         'origin',
@@ -174,7 +173,6 @@ describe('Test fetchDepth and fetchTags options', () => {
         'fetch',
         '--no-tags',
         '--prune',
-        '--progress',
         '--no-recurse-submodules',
         '--filter=filterValue',
         'origin',
@@ -213,7 +211,6 @@ describe('Test fetchDepth and fetchTags options', () => {
         'fetch',
         '--no-tags',
         '--prune',
-        '--progress',
         '--no-recurse-submodules',
         '--filter=filterValue',
         '--depth=1',
@@ -252,7 +249,6 @@ describe('Test fetchDepth and fetchTags options', () => {
         'protocol.version=2',
         'fetch',
         '--prune',
-        '--progress',
         '--no-recurse-submodules',
         '--filter=filterValue',
         '--depth=1',
@@ -263,4 +259,120 @@ describe('Test fetchDepth and fetchTags options', () => {
       expect.any(Object)
     )
   })
+
+  it('should call execGit with the correct arguments when showProgress is true', async () => {
+    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+
+    const workingDirectory = 'test'
+    const lfs = false
+    const doSparseCheckout = false
+    git = await commandManager.createCommandManager(
+      workingDirectory,
+      lfs,
+      doSparseCheckout
+    )
+    const refSpec = ['refspec1', 'refspec2']
+    const options = {
+      filter: 'filterValue',
+      showProgress: true
+    }
+
+    await git.fetch(refSpec, options)
+
+    expect(mockExec).toHaveBeenCalledWith(
+      expect.any(String),
+      [
+        '-c',
+        'protocol.version=2',
+        'fetch',
+        '--no-tags',
+        '--prune',
+        '--no-recurse-submodules',
+        '--progress',
+        '--filter=filterValue',
+        'origin',
+        'refspec1',
+        'refspec2'
+      ],
+      expect.any(Object)
+    )
+  })
+
+  it('should call execGit with the correct arguments when fetchDepth is 42 and showProgress is true', async () => {
+    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+
+    const workingDirectory = 'test'
+    const lfs = false
+    const doSparseCheckout = false
+    git = await commandManager.createCommandManager(
+      workingDirectory,
+      lfs,
+      doSparseCheckout
+    )
+    const refSpec = ['refspec1', 'refspec2']
+    const options = {
+      filter: 'filterValue',
+      fetchDepth: 42,
+      showProgress: true
+    }
+
+    await git.fetch(refSpec, options)
+
+    expect(mockExec).toHaveBeenCalledWith(
+      expect.any(String),
+      [
+        '-c',
+        'protocol.version=2',
+        'fetch',
+        '--no-tags',
+        '--prune',
+        '--no-recurse-submodules',
+        '--progress',
+        '--filter=filterValue',
+        '--depth=42',
+        'origin',
+        'refspec1',
+        'refspec2'
+      ],
+      expect.any(Object)
+    )
+  })
+
+  it('should call execGit with the correct arguments when fetchTags is true and showProgress is true', async () => {
+    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+
+    const workingDirectory = 'test'
+    const lfs = false
+    const doSparseCheckout = false
+    git = await commandManager.createCommandManager(
+      workingDirectory,
+      lfs,
+      doSparseCheckout
+    )
+    const refSpec = ['refspec1', 'refspec2']
+    const options = {
+      filter: 'filterValue',
+      fetchTags: true,
+      showProgress: true
+    }
+
+    await git.fetch(refSpec, options)
+
+    expect(mockExec).toHaveBeenCalledWith(
+      expect.any(String),
+      [
+        '-c',
+        'protocol.version=2',
+        'fetch',
+        '--prune',
+        '--no-recurse-submodules',
+        '--progress',
+        '--filter=filterValue',
+        'origin',
+        'refspec1',
+        'refspec2'
+      ],
+      expect.any(Object)
+    )
+  })
 })
diff --git a/__test__/input-helper.test.ts b/__test__/input-helper.test.ts
index aa58415..21932ca 100644
--- a/__test__/input-helper.test.ts
+++ b/__test__/input-helper.test.ts
@@ -83,6 +83,7 @@ describe('input-helper tests', () => {
     expect(settings.sparseCheckoutConeMode).toBe(true)
     expect(settings.fetchDepth).toBe(1)
     expect(settings.fetchTags).toBe(false)
+    expect(settings.showProgress).toBe(true)
     expect(settings.lfs).toBe(false)
     expect(settings.ref).toBe('refs/heads/some-ref')
     expect(settings.repositoryName).toBe('some-repo')
diff --git a/action.yml b/action.yml
index 7c67bc0..43d408d 100644
--- a/action.yml
+++ b/action.yml
@@ -68,6 +68,9 @@ inputs:
   fetch-tags:
     description: 'Whether to fetch tags, even if fetch-depth > 0.'
     default: false
+  show-progress:
+    description: 'Whether to show progress status output when fetching.'
+    default: true
   lfs:
     description: 'Whether to download Git-LFS files'
     default: false
diff --git a/dist/index.js b/dist/index.js
index 9e38490..67752ae 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -640,7 +640,10 @@ class GitCommandManager {
             if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) {
                 args.push('--no-tags');
             }
-            args.push('--prune', '--progress', '--no-recurse-submodules');
+            args.push('--prune', '--no-recurse-submodules');
+            if (options.showProgress) {
+                args.push('--progress');
+            }
             if (options.filter) {
                 args.push(`--filter=${options.filter}`);
             }
@@ -1739,6 +1742,10 @@ function getInputs() {
         result.fetchTags =
             (core.getInput('fetch-tags') || 'false').toUpperCase() === 'TRUE';
         core.debug(`fetch tags = ${result.fetchTags}`);
+        // Show fetch progress
+        result.showProgress =
+            (core.getInput('show-progress') || 'true').toUpperCase() === 'TRUE';
+        core.debug(`show progress = ${result.showProgress}`);
         // LFS
         result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE';
         core.debug(`lfs = ${result.lfs}`);
diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts
index 6ab807b..7752cfa 100644
--- a/src/git-command-manager.ts
+++ b/src/git-command-manager.ts
@@ -34,6 +34,7 @@ export interface IGitCommandManager {
       filter?: string
       fetchDepth?: number
       fetchTags?: boolean
+      showProgress?: boolean
     }
   ): Promise<void>
   getDefaultBranch(repositoryUrl: string): Promise<string>
@@ -241,14 +242,22 @@ class GitCommandManager {
 
   async fetch(
     refSpec: string[],
-    options: {filter?: string; fetchDepth?: number; fetchTags?: boolean}
+    options: {
+      filter?: string
+      fetchDepth?: number
+      fetchTags?: boolean
+      showProgress?: boolean
+    }
   ): Promise<void> {
     const args = ['-c', 'protocol.version=2', 'fetch']
     if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) {
       args.push('--no-tags')
     }
 
-    args.push('--prune', '--progress', '--no-recurse-submodules')
+    args.push('--prune', '--no-recurse-submodules')
+    if (options.showProgress) {
+      args.push('--progress')
+    }
 
     if (options.filter) {
       args.push(`--filter=${options.filter}`)
diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts
index 042563e..c1360b8 100644
--- a/src/git-source-provider.ts
+++ b/src/git-source-provider.ts
@@ -157,6 +157,7 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
       filter?: string
       fetchDepth?: number
       fetchTags?: boolean
+      showProgress?: boolean
     } = {}
     if (settings.sparseCheckout) fetchOptions.filter = 'blob:none'
     if (settings.fetchDepth <= 0) {
diff --git a/src/git-source-settings.ts b/src/git-source-settings.ts
index 9875d8d..2f80b5e 100644
--- a/src/git-source-settings.ts
+++ b/src/git-source-settings.ts
@@ -49,6 +49,11 @@ export interface IGitSourceSettings {
    */
   fetchTags: boolean
 
+  /**
+   * Indicates whether to use the --progress option when fetching
+   */
+  showProgress: boolean
+
   /**
    * Indicates whether to fetch LFS objects
    */
diff --git a/src/input-helper.ts b/src/input-helper.ts
index 631fbdb..be9cecd 100644
--- a/src/input-helper.ts
+++ b/src/input-helper.ts
@@ -105,6 +105,11 @@ export async function getInputs(): Promise<IGitSourceSettings> {
     (core.getInput('fetch-tags') || 'false').toUpperCase() === 'TRUE'
   core.debug(`fetch tags = ${result.fetchTags}`)
 
+  // Show fetch progress
+  result.showProgress =
+    (core.getInput('show-progress') || 'true').toUpperCase() === 'TRUE'
+  core.debug(`show progress = ${result.showProgress}`)
+
   // LFS
   result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE'
   core.debug(`lfs = ${result.lfs}`)