Browse Source

persist core.sshCommand for submodules (#184)

* persist core.sshCommand for submodules

* update verbiage; add comments

* fail when submodules or ssh-key and fallback to REST API
eric sciple 5 years ago
parent
commit
9a3a9ade82
6 changed files with 84 additions and 78 deletions
  1. 5 5
      README.md
  2. 32 54
      __test__/git-auth-helper.test.ts
  3. 3 3
      action.yml
  4. 19 8
      dist/index.js
  5. 15 8
      src/git-auth-helper.ts
  6. 10 0
      src/git-source-provider.ts

+ 5 - 5
README.md

@@ -49,19 +49,19 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
     # with the local git config, which enables your scripts to run authenticated git
     # with the local git config, which enables your scripts to run authenticated git
     # commands. The post-job step removes the PAT.
     # commands. The post-job step removes the PAT.
     #
     #
-    # We recommend creating a service account with the least permissions necessary.
-    # Also when generating a new PAT, select the least scopes necessary.
+    # We recommend using a service account with the least permissions necessary. Also
+    # when generating a new PAT, select the least scopes necessary.
     #
     #
     # [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
     # [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
     #
     #
     # Default: ${{ github.token }}
     # Default: ${{ github.token }}
     token: ''
     token: ''
 
 
-    # SSH key used to fetch the repository. SSH key is configured with the local git
-    # config, which enables your scripts to run authenticated git commands. The
+    # SSH key used to fetch the repository. The SSH key is configured with the local
+    # git config, which enables your scripts to run authenticated git commands. The
     # post-job step removes the SSH key.
     # post-job step removes the SSH key.
     #
     #
-    # We recommend creating a service account with the least permissions necessary.
+    # We recommend using a service account with the least permissions necessary.
     #
     #
     # [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
     # [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
     ssh-key: ''
     ssh-key: ''

+ 32 - 54
__test__/git-auth-helper.test.ts

@@ -320,6 +320,8 @@ describe('git-auth-helper tests', () => {
     ).toString()
     ).toString()
     expect(actualSshKeyContent).toBe(settings.sshKey + '\n')
     expect(actualSshKeyContent).toBe(settings.sshKey + '\n')
     if (!isWindows) {
     if (!isWindows) {
+      // Assert read/write for user, not group or others.
+      // Otherwise SSH client will error.
       expect((await fs.promises.stat(actualSshKeyPath)).mode & 0o777).toBe(
       expect((await fs.promises.stat(actualSshKeyPath)).mode & 0o777).toBe(
         0o600
         0o600
       )
       )
@@ -437,15 +439,16 @@ describe('git-auth-helper tests', () => {
     }
     }
   )
   )
 
 
-  const configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeyNotSet =
-    'configureSubmoduleAuth configures token when persist credentials true and SSH key not set'
+  const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeyNotSet =
+    'configureSubmoduleAuth configures submodules when persist credentials false and SSH key not set'
   it(
   it(
-    configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeyNotSet,
+    configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeyNotSet,
     async () => {
     async () => {
       // Arrange
       // Arrange
       await setup(
       await setup(
-        configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeyNotSet
+        configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeyNotSet
       )
       )
+      settings.persistCredentials = false
       settings.sshKey = ''
       settings.sshKey = ''
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
       await authHelper.configureAuth()
@@ -456,31 +459,30 @@ describe('git-auth-helper tests', () => {
       await authHelper.configureSubmoduleAuth()
       await authHelper.configureSubmoduleAuth()
 
 
       // Assert
       // Assert
-      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
-      expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
+      expect(mockSubmoduleForeach).toBeCalledTimes(1)
+      expect(mockSubmoduleForeach.mock.calls[0][0] as string).toMatch(
         /unset-all.*insteadOf/
         /unset-all.*insteadOf/
       )
       )
-      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
-      expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/url.*insteadOf/)
     }
     }
   )
   )
 
 
-  const configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet =
-    'configureSubmoduleAuth configures token when persist credentials true and SSH key set'
+  const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet =
+    'configureSubmoduleAuth configures submodules when persist credentials false and SSH key set'
   it(
   it(
-    configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet,
+    configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet,
     async () => {
     async () => {
       if (!sshPath) {
       if (!sshPath) {
         process.stdout.write(
         process.stdout.write(
-          `Skipped test "${configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet}". Executable 'ssh' not found in the PATH.\n`
+          `Skipped test "${configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet}". Executable 'ssh' not found in the PATH.\n`
         )
         )
         return
         return
       }
       }
 
 
       // Arrange
       // Arrange
       await setup(
       await setup(
-        configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet
+        configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet
       )
       )
+      settings.persistCredentials = false
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
       await authHelper.configureAuth()
       const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
       const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
@@ -490,24 +492,23 @@ describe('git-auth-helper tests', () => {
       await authHelper.configureSubmoduleAuth()
       await authHelper.configureSubmoduleAuth()
 
 
       // Assert
       // Assert
-      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2)
+      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(1)
       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
         /unset-all.*insteadOf/
         /unset-all.*insteadOf/
       )
       )
-      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
     }
     }
   )
   )
 
 
-  const configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse =
-    'configureSubmoduleAuth does not configure token when persist credentials false'
+  const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeyNotSet =
+    'configureSubmoduleAuth configures submodules when persist credentials true and SSH key not set'
   it(
   it(
-    configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse,
+    configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeyNotSet,
     async () => {
     async () => {
       // Arrange
       // Arrange
       await setup(
       await setup(
-        configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse
+        configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeyNotSet
       )
       )
-      settings.persistCredentials = false
+      settings.sshKey = ''
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
       await authHelper.configureAuth()
       const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
       const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
@@ -517,28 +518,30 @@ describe('git-auth-helper tests', () => {
       await authHelper.configureSubmoduleAuth()
       await authHelper.configureSubmoduleAuth()
 
 
       // Assert
       // Assert
-      expect(mockSubmoduleForeach).toBeCalledTimes(1)
-      expect(mockSubmoduleForeach.mock.calls[0][0] as string).toMatch(
+      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
+      expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
         /unset-all.*insteadOf/
         /unset-all.*insteadOf/
       )
       )
+      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
+      expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/url.*insteadOf/)
     }
     }
   )
   )
 
 
-  const configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet =
-    'configureSubmoduleAuth does not configure URL insteadOf when persist credentials true and SSH key set'
+  const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet =
+    'configureSubmoduleAuth configures submodules when persist credentials true and SSH key set'
   it(
   it(
-    configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet,
+    configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet,
     async () => {
     async () => {
       if (!sshPath) {
       if (!sshPath) {
         process.stdout.write(
         process.stdout.write(
-          `Skipped test "${configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet}". Executable 'ssh' not found in the PATH.\n`
+          `Skipped test "${configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet}". Executable 'ssh' not found in the PATH.\n`
         )
         )
         return
         return
       }
       }
 
 
       // Arrange
       // Arrange
       await setup(
       await setup(
-        configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet
+        configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet
       )
       )
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
       await authHelper.configureAuth()
@@ -549,37 +552,12 @@ describe('git-auth-helper tests', () => {
       await authHelper.configureSubmoduleAuth()
       await authHelper.configureSubmoduleAuth()
 
 
       // Assert
       // Assert
-      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2)
+      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
         /unset-all.*insteadOf/
         /unset-all.*insteadOf/
       )
       )
       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
-    }
-  )
-
-  const configureSubmoduleAuth_removesUrlInsteadOfWhenPersistCredentialsFalse =
-    'configureSubmoduleAuth removes URL insteadOf when persist credentials false'
-  it(
-    configureSubmoduleAuth_removesUrlInsteadOfWhenPersistCredentialsFalse,
-    async () => {
-      // Arrange
-      await setup(
-        configureSubmoduleAuth_removesUrlInsteadOfWhenPersistCredentialsFalse
-      )
-      settings.persistCredentials = false
-      const authHelper = gitAuthHelper.createAuthHelper(git, settings)
-      await authHelper.configureAuth()
-      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
-      mockSubmoduleForeach.mockClear() // reset calls
-
-      // Act
-      await authHelper.configureSubmoduleAuth()
-
-      // Assert
-      expect(mockSubmoduleForeach).toBeCalledTimes(1)
-      expect(mockSubmoduleForeach.mock.calls[0][0] as string).toMatch(
-        /unset-all.*insteadOf/
-      )
+      expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/)
     }
     }
   )
   )
 
 

+ 3 - 3
action.yml

@@ -16,7 +16,7 @@ inputs:
       commands. The post-job step removes the PAT.
       commands. The post-job step removes the PAT.
 
 
 
 
-      We recommend creating a service account with the least permissions necessary.
+      We recommend using a service account with the least permissions necessary.
       Also when generating a new PAT, select the least scopes necessary.
       Also when generating a new PAT, select the least scopes necessary.
 
 
 
 
@@ -24,12 +24,12 @@ inputs:
     default: ${{ github.token }}
     default: ${{ github.token }}
   ssh-key:
   ssh-key:
     description: >
     description: >
-      SSH key used to fetch the repository. SSH key is configured with the local
+      SSH key used to fetch the repository. The SSH key is configured with the local
       git config, which enables your scripts to run authenticated git commands.
       git config, which enables your scripts to run authenticated git commands.
       The post-job step removes the SSH key.
       The post-job step removes the SSH key.
 
 
 
 
-      We recommend creating a service account with the least permissions necessary.
+      We recommend using a service account with the least permissions necessary.
 
 
 
 
       [Learn more about creating and using
       [Learn more about creating and using

+ 19 - 8
dist/index.js

@@ -5122,6 +5122,7 @@ class GitAuthHelper {
         this.tokenConfigKey = `http.https://${HOSTNAME}/.extraheader`;
         this.tokenConfigKey = `http.https://${HOSTNAME}/.extraheader`;
         this.insteadOfKey = `url.https://${HOSTNAME}/.insteadOf`;
         this.insteadOfKey = `url.https://${HOSTNAME}/.insteadOf`;
         this.insteadOfValue = `git@${HOSTNAME}:`;
         this.insteadOfValue = `git@${HOSTNAME}:`;
+        this.sshCommand = '';
         this.sshKeyPath = '';
         this.sshKeyPath = '';
         this.sshKnownHostsPath = '';
         this.sshKnownHostsPath = '';
         this.temporaryHomePath = '';
         this.temporaryHomePath = '';
@@ -5205,8 +5206,12 @@ class GitAuthHelper {
                     core.debug(`Replacing token placeholder in '${configPath}'`);
                     core.debug(`Replacing token placeholder in '${configPath}'`);
                     this.replaceTokenPlaceholder(configPath);
                     this.replaceTokenPlaceholder(configPath);
                 }
                 }
-                // Configure HTTPS instead of SSH
-                if (!this.settings.sshKey) {
+                if (this.settings.sshKey) {
+                    // Configure core.sshCommand
+                    yield this.git.submoduleForeach(`git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`, this.settings.nestedSubmodules);
+                }
+                else {
+                    // Configure HTTPS instead of SSH
                     yield this.git.submoduleForeach(`git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`, this.settings.nestedSubmodules);
                     yield this.git.submoduleForeach(`git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`, this.settings.nestedSubmodules);
                 }
                 }
             }
             }
@@ -5268,16 +5273,16 @@ class GitAuthHelper {
             yield fs.promises.writeFile(this.sshKnownHostsPath, knownHosts);
             yield fs.promises.writeFile(this.sshKnownHostsPath, knownHosts);
             // Configure GIT_SSH_COMMAND
             // Configure GIT_SSH_COMMAND
             const sshPath = yield io.which('ssh', true);
             const sshPath = yield io.which('ssh', true);
-            let sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(this.sshKeyPath)}"`;
+            this.sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(this.sshKeyPath)}"`;
             if (this.settings.sshStrict) {
             if (this.settings.sshStrict) {
-                sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no';
+                this.sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no';
             }
             }
-            sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(this.sshKnownHostsPath)}"`;
-            core.info(`Temporarily overriding GIT_SSH_COMMAND=${sshCommand}`);
-            this.git.setEnvironmentVariable('GIT_SSH_COMMAND', sshCommand);
+            this.sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(this.sshKnownHostsPath)}"`;
+            core.info(`Temporarily overriding GIT_SSH_COMMAND=${this.sshCommand}`);
+            this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand);
             // Configure core.sshCommand
             // Configure core.sshCommand
             if (this.settings.persistCredentials) {
             if (this.settings.persistCredentials) {
-                yield this.git.config(SSH_COMMAND_KEY, sshCommand);
+                yield this.git.config(SSH_COMMAND_KEY, this.sshCommand);
             }
             }
         });
         });
     }
     }
@@ -5820,6 +5825,12 @@ function getSource(settings) {
             // Downloading using REST API
             // Downloading using REST API
             core.info(`The repository will be downloaded using the GitHub REST API`);
             core.info(`The repository will be downloaded using the GitHub REST API`);
             core.info(`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`);
             core.info(`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`);
+            if (settings.submodules) {
+                throw new Error(`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
+            }
+            else if (settings.sshKey) {
+                throw new Error(`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
+            }
             yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
             yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
             return;
             return;
         }
         }

+ 15 - 8
src/git-auth-helper.ts

@@ -37,6 +37,7 @@ class GitAuthHelper {
   private readonly tokenPlaceholderConfigValue: string
   private readonly tokenPlaceholderConfigValue: string
   private readonly insteadOfKey: string = `url.https://${HOSTNAME}/.insteadOf`
   private readonly insteadOfKey: string = `url.https://${HOSTNAME}/.insteadOf`
   private readonly insteadOfValue: string = `git@${HOSTNAME}:`
   private readonly insteadOfValue: string = `git@${HOSTNAME}:`
+  private sshCommand = ''
   private sshKeyPath = ''
   private sshKeyPath = ''
   private sshKnownHostsPath = ''
   private sshKnownHostsPath = ''
   private temporaryHomePath = ''
   private temporaryHomePath = ''
@@ -144,8 +145,14 @@ class GitAuthHelper {
         this.replaceTokenPlaceholder(configPath)
         this.replaceTokenPlaceholder(configPath)
       }
       }
 
 
-      // Configure HTTPS instead of SSH
-      if (!this.settings.sshKey) {
+      if (this.settings.sshKey) {
+        // Configure core.sshCommand
+        await this.git.submoduleForeach(
+          `git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`,
+          this.settings.nestedSubmodules
+        )
+      } else {
+        // Configure HTTPS instead of SSH
         await this.git.submoduleForeach(
         await this.git.submoduleForeach(
           `git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`,
           `git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`,
           this.settings.nestedSubmodules
           this.settings.nestedSubmodules
@@ -218,21 +225,21 @@ class GitAuthHelper {
 
 
     // Configure GIT_SSH_COMMAND
     // Configure GIT_SSH_COMMAND
     const sshPath = await io.which('ssh', true)
     const sshPath = await io.which('ssh', true)
-    let sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(
+    this.sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(
       this.sshKeyPath
       this.sshKeyPath
     )}"`
     )}"`
     if (this.settings.sshStrict) {
     if (this.settings.sshStrict) {
-      sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no'
+      this.sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no'
     }
     }
-    sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(
+    this.sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(
       this.sshKnownHostsPath
       this.sshKnownHostsPath
     )}"`
     )}"`
-    core.info(`Temporarily overriding GIT_SSH_COMMAND=${sshCommand}`)
-    this.git.setEnvironmentVariable('GIT_SSH_COMMAND', sshCommand)
+    core.info(`Temporarily overriding GIT_SSH_COMMAND=${this.sshCommand}`)
+    this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand)
 
 
     // Configure core.sshCommand
     // Configure core.sshCommand
     if (this.settings.persistCredentials) {
     if (this.settings.persistCredentials) {
-      await this.git.config(SSH_COMMAND_KEY, sshCommand)
+      await this.git.config(SSH_COMMAND_KEY, this.sshCommand)
     }
     }
   }
   }
 
 

+ 10 - 0
src/git-source-provider.ts

@@ -57,6 +57,16 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
     core.info(
     core.info(
       `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
       `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
     )
     )
+    if (settings.submodules) {
+      throw new Error(
+        `Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
+      )
+    } else if (settings.sshKey) {
+      throw new Error(
+        `Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
+      )
+    }
+
     await githubApiHelper.downloadRepository(
     await githubApiHelper.downloadRepository(
       settings.authToken,
       settings.authToken,
       settings.repositoryOwner,
       settings.repositoryOwner,