mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-28 16:18:37 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			e4894fca20
			...
			317d15a1e8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 317d15a1e8 | ||
|   | 1929737c8e | ||
|   | dc519229d3 | ||
|   | c9518fb408 | ||
|   | f8060825ea | ||
|   | 3292e202f3 | 
|  | @ -595,18 +595,15 @@ describe('git-auth-helper tests', () => { | |||
|       await authHelper.configureSubmoduleAuth() | ||||
| 
 | ||||
|       // Assert
 | ||||
|       // Should get submodule config paths (1 call) and configure insteadOf (2 calls for two values)
 | ||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4) | ||||
|       // Should configure insteadOf (2 calls for two values)
 | ||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) | ||||
|       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( | ||||
|         /unset-all.*insteadOf/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch( | ||||
|         /show-origin.*remote\.origin\.url/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( | ||||
|         /url.*insteadOf.*git@github.com:/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch( | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( | ||||
|         /url.*insteadOf.*org-123456@github.com:/ | ||||
|       ) | ||||
|     } | ||||
|  | @ -637,15 +634,12 @@ describe('git-auth-helper tests', () => { | |||
|       await authHelper.configureSubmoduleAuth() | ||||
| 
 | ||||
|       // Assert
 | ||||
|       // Should get submodule config paths (1 call) and configure sshCommand (1 call)
 | ||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) | ||||
|       // Should configure sshCommand (1 call)
 | ||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2) | ||||
|       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( | ||||
|         /unset-all.*insteadOf/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch( | ||||
|         /show-origin.*remote\.origin\.url/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/core\.sshCommand/) | ||||
|     } | ||||
|   ) | ||||
| 
 | ||||
|  | @ -768,6 +762,28 @@ describe('git-auth-helper tests', () => { | |||
|       } | ||||
|     } | ||||
|   }) | ||||
| 
 | ||||
|   const testCredentialsConfigPath_matchesCredentialsConfigPaths = | ||||
|     'testCredentialsConfigPath matches credentials config paths' | ||||
|   it(testCredentialsConfigPath_matchesCredentialsConfigPaths, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(testCredentialsConfigPath_matchesCredentialsConfigPaths) | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|      | ||||
|     // Get a real credentials config path
 | ||||
|     const credentialsConfigPath = await (authHelper as any).getCredentialsConfigPath() | ||||
|      | ||||
|     // Act & Assert
 | ||||
|     expect((authHelper as any).testCredentialsConfigPath(credentialsConfigPath)).toBe(true) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-12345678-abcd-1234-5678-123456789012.config')).toBe(true) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config')).toBe(true) | ||||
|      | ||||
|     // Test invalid paths
 | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/other-config.config')).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-invalid.config')).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('/some/path/git-credentials-.config')).toBe(false) | ||||
|     expect((authHelper as any).testCredentialsConfigPath('')).toBe(false) | ||||
|   }) | ||||
| }) | ||||
| 
 | ||||
| async function setup(testName: string): Promise<void> { | ||||
|  | @ -834,6 +850,7 @@ async function setup(testName: string): Promise<void> { | |||
|     env: {}, | ||||
|     fetch: jest.fn(), | ||||
|     getDefaultBranch: jest.fn(), | ||||
|     getSubmoduleConfigPaths: jest.fn(async () => []), | ||||
|     getWorkingDirectory: jest.fn(() => workspace), | ||||
|     init: jest.fn(), | ||||
|     isDetached: jest.fn(), | ||||
|  |  | |||
|  | @ -471,6 +471,7 @@ async function setup(testName: string): Promise<void> { | |||
|     configExists: jest.fn(), | ||||
|     fetch: jest.fn(), | ||||
|     getDefaultBranch: jest.fn(), | ||||
|     getSubmoduleConfigPaths: jest.fn(async () => []), | ||||
|     getWorkingDirectory: jest.fn(() => repositoryPath), | ||||
|     init: jest.fn(), | ||||
|     isDetached: jest.fn(), | ||||
|  |  | |||
							
								
								
									
										256
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										256
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							|  | @ -163,7 +163,6 @@ class GitAuthHelper { | |||
|         this.sshKnownHostsPath = ''; | ||||
|         this.temporaryHomePath = ''; | ||||
|         this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
 | ||||
|         this.credentialsIncludeKeys = []; // Track includeIf config keys for cleanup
 | ||||
|         this.git = gitCommandManager; | ||||
|         this.settings = gitSourceSettings || {}; | ||||
|         // Token auth header
 | ||||
|  | @ -189,20 +188,6 @@ class GitAuthHelper { | |||
|             yield this.configureToken(); | ||||
|         }); | ||||
|     } | ||||
|     getCredentialsConfigPath() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             if (this.credentialsConfigPath) { | ||||
|                 return this.credentialsConfigPath; | ||||
|             } | ||||
|             const runnerTemp = process.env['RUNNER_TEMP'] || ''; | ||||
|             assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||
|             // Create a unique filename for this checkout instance
 | ||||
|             const configFileName = `git-credentials-${(0, uuid_1.v4)()}.config`; | ||||
|             this.credentialsConfigPath = path.join(runnerTemp, configFileName); | ||||
|             core.debug(`Credentials config path: ${this.credentialsConfigPath}`); | ||||
|             return this.credentialsConfigPath; | ||||
|         }); | ||||
|     } | ||||
|     configureTempGlobalConfig() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             var _a; | ||||
|  | @ -282,10 +267,7 @@ class GitAuthHelper { | |||
|                 relativePath = relativePath.replace(/\\/g, '/'); | ||||
|                 const containerRepoPath = path.posix.join('/github/workspace', relativePath); | ||||
|                 // Get submodule config file paths.
 | ||||
|                 // Use `--show-origin` to get the config file path for each submodule.
 | ||||
|                 const output = yield this.git.submoduleForeach(`git config --local --show-origin --name-only --get-regexp remote.origin.url`, this.settings.nestedSubmodules); | ||||
|                 // Extract config file paths from the output (lines starting with "file:").
 | ||||
|                 const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; | ||||
|                 const configPaths = yield this.git.getSubmoduleConfigPaths(this.settings.nestedSubmodules); | ||||
|                 // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | ||||
|                 // Configure both host and container paths to support Docker container actions.
 | ||||
|                 for (const configPath of configPaths) { | ||||
|  | @ -329,6 +311,10 @@ class GitAuthHelper { | |||
|             } | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Configures SSH authentication by writing the SSH key and known hosts, | ||||
|      * and setting up the GIT_SSH_COMMAND environment variable. | ||||
|      */ | ||||
|     configureSsh() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             if (!this.settings.sshKey) { | ||||
|  | @ -385,6 +371,11 @@ class GitAuthHelper { | |||
|             } | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Configures token-based authentication by creating a credentials config file | ||||
|      * and setting up includeIf entries to reference it. | ||||
|      * @param globalConfig Whether to configure global config instead of local | ||||
|      */ | ||||
|     configureToken(globalConfig) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             // Get the credentials config file path in RUNNER_TEMP
 | ||||
|  | @ -395,7 +386,15 @@ class GitAuthHelper { | |||
|             // https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||
|             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, false, credentialsConfigPath); | ||||
|             // Replace the placeholder in the credentials config file
 | ||||
|             yield this.replaceTokenPlaceholder(credentialsConfigPath); | ||||
|             let content = (yield fs.promises.readFile(credentialsConfigPath)).toString(); | ||||
|             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); | ||||
|             if (placeholderIndex < 0 || | ||||
|                 placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { | ||||
|                 throw new Error(`Unable to replace auth placeholder in ${credentialsConfigPath}`); | ||||
|             } | ||||
|             assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined'); | ||||
|             content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue); | ||||
|             yield fs.promises.writeFile(credentialsConfigPath, content); | ||||
|             // Add include or includeIf to reference the credentials config
 | ||||
|             if (globalConfig) { | ||||
|                 // Global config file is temporary
 | ||||
|  | @ -408,7 +407,6 @@ class GitAuthHelper { | |||
|                 // Configure host includeIf
 | ||||
|                 const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`; | ||||
|                 yield this.git.config(hostIncludeKey, credentialsConfigPath); | ||||
|                 this.credentialsIncludeKeys.push(hostIncludeKey); | ||||
|                 // Container git directory
 | ||||
|                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||
|                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||
|  | @ -421,31 +419,39 @@ class GitAuthHelper { | |||
|                 // Configure container includeIf
 | ||||
|                 const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`; | ||||
|                 yield this.git.config(containerIncludeKey, containerCredentialsPath); | ||||
|                 this.credentialsIncludeKeys.push(containerIncludeKey); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     replaceTokenPlaceholder(configPath) { | ||||
|     /** | ||||
|      * Gets or creates the path to the credentials config file in RUNNER_TEMP. | ||||
|      * @returns The absolute path to the credentials config file | ||||
|      */ | ||||
|     getCredentialsConfigPath() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             assert.ok(configPath, 'configPath is not defined'); | ||||
|             let content = (yield fs.promises.readFile(configPath)).toString(); | ||||
|             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); | ||||
|             if (placeholderIndex < 0 || | ||||
|                 placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { | ||||
|                 throw new Error(`Unable to replace auth placeholder in ${configPath}`); | ||||
|             if (this.credentialsConfigPath) { | ||||
|                 return this.credentialsConfigPath; | ||||
|             } | ||||
|             assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined'); | ||||
|             content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue); | ||||
|             yield fs.promises.writeFile(configPath, content); | ||||
|             const runnerTemp = process.env['RUNNER_TEMP'] || ''; | ||||
|             assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||
|             // Create a unique filename for this checkout instance
 | ||||
|             const configFileName = `git-credentials-${(0, uuid_1.v4)()}.config`; | ||||
|             this.credentialsConfigPath = path.join(runnerTemp, configFileName); | ||||
|             core.debug(`Credentials config path: ${this.credentialsConfigPath}`); | ||||
|             return this.credentialsConfigPath; | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Removes SSH authentication configuration by cleaning up SSH keys, | ||||
|      * known hosts files, and SSH command configurations. | ||||
|      */ | ||||
|     removeSsh() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             var _a; | ||||
|             var _a, _b; | ||||
|             // SSH key
 | ||||
|             const keyPath = this.sshKeyPath || stateHelper.SshKeyPath; | ||||
|             if (keyPath) { | ||||
|                 try { | ||||
|                     core.info(`Removing SSH key '${keyPath}'`); | ||||
|                     yield io.rmRF(keyPath); | ||||
|                 } | ||||
|                 catch (err) { | ||||
|  | @ -457,60 +463,68 @@ class GitAuthHelper { | |||
|             const knownHostsPath = this.sshKnownHostsPath || stateHelper.SshKnownHostsPath; | ||||
|             if (knownHostsPath) { | ||||
|                 try { | ||||
|                     core.info(`Removing SSH known hosts '${knownHostsPath}'`); | ||||
|                     yield io.rmRF(knownHostsPath); | ||||
|                 } | ||||
|                 catch (_b) { | ||||
|                     // Intentionally empty
 | ||||
|                 catch (err) { | ||||
|                     core.debug(`${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`); | ||||
|                     core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`); | ||||
|                 } | ||||
|             } | ||||
|             // SSH command
 | ||||
|             core.info("Removing SSH command configuration"); | ||||
|             yield this.removeGitConfig(SSH_COMMAND_KEY); | ||||
|             yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY); | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Removes token-based authentication by cleaning up HTTP headers, | ||||
|      * includeIf entries, and credentials config files. | ||||
|      */ | ||||
|     removeToken() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             var _a; | ||||
|             // Remove HTTP extra header
 | ||||
|             core.info("Removing HTTP extra header"); | ||||
|             yield this.removeGitConfig(this.tokenConfigKey); | ||||
|             yield this.removeSubmoduleGitConfig(this.tokenConfigKey); | ||||
|             // Collect credentials config paths that need to be removed
 | ||||
|             const credentialsPaths = new Set(); | ||||
|             // Remove includeIf entries that point to git-credentials-*.config files
 | ||||
|             // This is more aggressive than tracking keys, but necessary since cleanup
 | ||||
|             // runs in a post-step where this.credentialsIncludeKeys is empty
 | ||||
|             try { | ||||
|                 // Get all includeIf.gitdir keys
 | ||||
|                 const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:'); | ||||
|                 for (const key of keys) { | ||||
|                     // Get all values for this key
 | ||||
|                     const values = yield this.git.tryGetConfigValues(key); | ||||
|                     if (values.length > 0) { | ||||
|                         // Remove only values that match git-credentials-<uuid>.config pattern
 | ||||
|                         for (const value of values) { | ||||
|                             if (/git-credentials-[0-9a-f-]+\.config$/i.test(value)) { | ||||
|                                 yield this.git.tryConfigUnsetValue(key, value); | ||||
|                             } | ||||
|                         } | ||||
|             core.info("Removing includeIf entries pointing to credentials config files"); | ||||
|             const mainCredentialsPaths = yield this.removeIncludeIfCredentials(); | ||||
|             mainCredentialsPaths.forEach(path => credentialsPaths.add(path)); | ||||
|             // Remove submodule includeIf entries that point to git-credentials-*.config files
 | ||||
|             const submoduleConfigPaths = yield this.git.getSubmoduleConfigPaths(true); | ||||
|             for (const configPath of submoduleConfigPaths) { | ||||
|                 const submoduleCredentialsPaths = yield this.removeIncludeIfCredentials(configPath); | ||||
|                 submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path)); | ||||
|             } | ||||
|             // Remove credentials config files
 | ||||
|             for (const credentialsPath of credentialsPaths) { | ||||
|                 // Only remove credentials config files if they are under RUNNER_TEMP
 | ||||
|                 const runnerTemp = process.env['RUNNER_TEMP']; | ||||
|                 assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||
|                 if (credentialsPath.startsWith(runnerTemp)) { | ||||
|                     try { | ||||
|                         core.info(`Removing credentials config '${credentialsPath}'`); | ||||
|                         yield io.rmRF(credentialsPath); | ||||
|                     } | ||||
|                     catch (err) { | ||||
|                         core.debug(`${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`); | ||||
|                         core.warning(`Failed to remove credentials config '${credentialsPath}'`); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (err) { | ||||
|                 // Ignore errors - this is cleanup code
 | ||||
|                 core.debug(`Error during includeIf cleanup: ${err}`); | ||||
|             } | ||||
|             // Remove submodule includeIf
 | ||||
|             yield this.git.submoduleForeach(`sh -c "git config --local --get-regexp '^includeIf\\.' && git config --local --remove-section includeIf || :"`, true); | ||||
|             // Remove credentials config file
 | ||||
|             if (this.credentialsConfigPath) { | ||||
|                 try { | ||||
|                     yield io.rmRF(this.credentialsConfigPath); | ||||
|                 } | ||||
|                 catch (err) { | ||||
|                     core.debug(`${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`); | ||||
|                     core.warning(`Failed to remove credentials config '${this.credentialsConfigPath}'`); | ||||
|                 else { | ||||
|                     core.debug(`Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP`); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Removes a git config key from the local repository config. | ||||
|      * @param configKey The git config key to remove | ||||
|      */ | ||||
|     removeGitConfig(configKey) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             if ((yield this.git.configExists(configKey)) && | ||||
|  | @ -520,6 +534,10 @@ class GitAuthHelper { | |||
|             } | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Removes a git config key from all submodule configs. | ||||
|      * @param configKey The git config key to remove | ||||
|      */ | ||||
|     removeSubmoduleGitConfig(configKey) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const pattern = regexpHelper.escape(configKey); | ||||
|  | @ -528,6 +546,51 @@ class GitAuthHelper { | |||
|             `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true); | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Removes includeIf entries that point to git-credentials-*.config files. | ||||
|      * @param configPath Optional path to a specific git config file to operate on | ||||
|      * @returns Array of unique credentials config file paths that were found and removed | ||||
|      */ | ||||
|     removeIncludeIfCredentials(configPath) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const credentialsPaths = new Set(); | ||||
|             try { | ||||
|                 // Get all includeIf.gitdir keys
 | ||||
|                 const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, configPath); | ||||
|                 for (const key of keys) { | ||||
|                     // Get all values for this key
 | ||||
|                     const values = yield this.git.tryGetConfigValues(key, false, configPath); | ||||
|                     if (values.length > 0) { | ||||
|                         // Remove only values that match git-credentials-<uuid>.config pattern
 | ||||
|                         for (const value of values) { | ||||
|                             if (this.testCredentialsConfigPath(value)) { | ||||
|                                 credentialsPaths.add(value); | ||||
|                                 yield this.git.tryConfigUnsetValue(key, value, false, configPath); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (err) { | ||||
|                 // Ignore errors - this is cleanup code
 | ||||
|                 if (configPath) { | ||||
|                     core.debug(`Error during includeIf cleanup for ${configPath}: ${err}`); | ||||
|                 } | ||||
|                 else { | ||||
|                     core.debug(`Error during includeIf cleanup: ${err}`); | ||||
|                 } | ||||
|             } | ||||
|             return Array.from(credentialsPaths); | ||||
|         }); | ||||
|     } | ||||
|     /** | ||||
|      * Tests if a path matches the git-credentials-*.config pattern. | ||||
|      * @param path The path to test | ||||
|      * @returns True if the path matches the credentials config pattern | ||||
|      */ | ||||
|     testCredentialsConfigPath(path) { | ||||
|         return /git-credentials-[0-9a-f-]+\.config$/i.test(path); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -810,6 +873,16 @@ class GitCommandManager { | |||
|             throw new Error('Unexpected output when retrieving default branch'); | ||||
|         }); | ||||
|     } | ||||
|     getSubmoduleConfigPaths(recursive) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             // Get submodule config file paths.
 | ||||
|             // Use `--show-origin` to get the config file path for each submodule.
 | ||||
|             const output = yield this.submoduleForeach(`git config --local --show-origin --name-only --get-regexp remote.origin.url`, recursive); | ||||
|             // Extract config file paths from the output (lines starting with "file:").
 | ||||
|             const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; | ||||
|             return configPaths; | ||||
|         }); | ||||
|     } | ||||
|     getWorkingDirectory() { | ||||
|         return this.workingDirectory; | ||||
|     } | ||||
|  | @ -940,15 +1013,17 @@ class GitCommandManager { | |||
|             return output.exitCode === 0; | ||||
|         }); | ||||
|     } | ||||
|     tryConfigUnsetValue(configKey, configValue, globalConfig) { | ||||
|     tryConfigUnsetValue(configKey, configValue, globalConfig, configFile) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const output = yield this.execGit([ | ||||
|                 'config', | ||||
|                 globalConfig ? '--global' : '--local', | ||||
|                 '--unset', | ||||
|                 configKey, | ||||
|                 configValue | ||||
|             ], true); | ||||
|             const args = ['config']; | ||||
|             if (configFile) { | ||||
|                 args.push('--file', configFile); | ||||
|             } | ||||
|             else { | ||||
|                 args.push(globalConfig ? '--global' : '--local'); | ||||
|             } | ||||
|             args.push('--unset', configKey, configValue); | ||||
|             const output = yield this.execGit(args, true); | ||||
|             return output.exitCode === 0; | ||||
|         }); | ||||
|     } | ||||
|  | @ -971,29 +1046,34 @@ class GitCommandManager { | |||
|             return stdout; | ||||
|         }); | ||||
|     } | ||||
|     tryGetConfigValues(configKey, globalConfig) { | ||||
|     tryGetConfigValues(configKey, globalConfig, configFile) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const output = yield this.execGit([ | ||||
|                 'config', | ||||
|                 globalConfig ? '--global' : '--local', | ||||
|                 '--get-all', | ||||
|                 configKey | ||||
|             ], true); | ||||
|             const args = ['config']; | ||||
|             if (configFile) { | ||||
|                 args.push('--file', configFile); | ||||
|             } | ||||
|             else { | ||||
|                 args.push(globalConfig ? '--global' : '--local'); | ||||
|             } | ||||
|             args.push('--get-all', configKey); | ||||
|             const output = yield this.execGit(args, true); | ||||
|             if (output.exitCode !== 0) { | ||||
|                 return []; | ||||
|             } | ||||
|             return output.stdout.trim().split('\n').filter(value => value.trim()); | ||||
|         }); | ||||
|     } | ||||
|     tryGetConfigKeys(pattern, globalConfig) { | ||||
|     tryGetConfigKeys(pattern, globalConfig, configFile) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const output = yield this.execGit([ | ||||
|                 'config', | ||||
|                 globalConfig ? '--global' : '--local', | ||||
|                 '--name-only', | ||||
|                 '--get-regexp', | ||||
|                 pattern | ||||
|             ], true); | ||||
|             const args = ['config']; | ||||
|             if (configFile) { | ||||
|                 args.push('--file', configFile); | ||||
|             } | ||||
|             else { | ||||
|                 args.push(globalConfig ? '--global' : '--local'); | ||||
|             } | ||||
|             args.push('--name-only', '--get-regexp', pattern); | ||||
|             const output = yield this.execGit(args, true); | ||||
|             if (output.exitCode !== 0) { | ||||
|                 return []; | ||||
|             } | ||||
|  |  | |||
|  | @ -44,7 +44,6 @@ class GitAuthHelper { | |||
|   private sshKnownHostsPath = '' | ||||
|   private temporaryHomePath = '' | ||||
|   private credentialsConfigPath = '' // Path to separate credentials config file in RUNNER_TEMP
 | ||||
|   private credentialsIncludeKeys: string[] = [] // Track includeIf config keys for cleanup
 | ||||
| 
 | ||||
|   constructor( | ||||
|     gitCommandManager: IGitCommandManager, | ||||
|  | @ -83,22 +82,6 @@ class GitAuthHelper { | |||
|     await this.configureToken() | ||||
|   } | ||||
| 
 | ||||
|   private async getCredentialsConfigPath(): Promise<string> { | ||||
|     if (this.credentialsConfigPath) { | ||||
|       return this.credentialsConfigPath | ||||
|     } | ||||
| 
 | ||||
|     const runnerTemp = process.env['RUNNER_TEMP'] || '' | ||||
|     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||
| 
 | ||||
|     // Create a unique filename for this checkout instance
 | ||||
|     const configFileName = `git-credentials-${uuid()}.config` | ||||
|     this.credentialsConfigPath = path.join(runnerTemp, configFileName) | ||||
| 
 | ||||
|     core.debug(`Credentials config path: ${this.credentialsConfigPath}`) | ||||
|     return this.credentialsConfigPath | ||||
|   } | ||||
| 
 | ||||
|   async configureTempGlobalConfig(): Promise<string> { | ||||
|     // Already setup global config
 | ||||
|     if (this.temporaryHomePath?.length > 0) { | ||||
|  | @ -192,16 +175,10 @@ class GitAuthHelper { | |||
|       ) | ||||
| 
 | ||||
|       // Get submodule config file paths.
 | ||||
|       // Use `--show-origin` to get the config file path for each submodule.
 | ||||
|       const output = await this.git.submoduleForeach( | ||||
|         `git config --local --show-origin --name-only --get-regexp remote.origin.url`, | ||||
|       const configPaths = await this.git.getSubmoduleConfigPaths( | ||||
|         this.settings.nestedSubmodules | ||||
|       ) | ||||
| 
 | ||||
|       // Extract config file paths from the output (lines starting with "file:").
 | ||||
|       const configPaths = | ||||
|         output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] | ||||
| 
 | ||||
|       // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | ||||
|       // Configure both host and container paths to support Docker container actions.
 | ||||
|       for (const configPath of configPaths) { | ||||
|  | @ -268,6 +245,10 @@ class GitAuthHelper { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Configures SSH authentication by writing the SSH key and known hosts, | ||||
|    * and setting up the GIT_SSH_COMMAND environment variable. | ||||
|    */ | ||||
|   private async configureSsh(): Promise<void> { | ||||
|     if (!this.settings.sshKey) { | ||||
|       return | ||||
|  | @ -339,6 +320,11 @@ class GitAuthHelper { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Configures token-based authentication by creating a credentials config file | ||||
|    * and setting up includeIf entries to reference it. | ||||
|    * @param globalConfig Whether to configure global config instead of local | ||||
|    */ | ||||
|   private async configureToken(globalConfig?: boolean): Promise<void> { | ||||
|     // Get the credentials config file path in RUNNER_TEMP
 | ||||
|     const credentialsConfigPath = await this.getCredentialsConfigPath() | ||||
|  | @ -356,7 +342,20 @@ class GitAuthHelper { | |||
|     ) | ||||
| 
 | ||||
|     // Replace the placeholder in the credentials config file
 | ||||
|     await this.replaceTokenPlaceholder(credentialsConfigPath) | ||||
|     let content = (await fs.promises.readFile(credentialsConfigPath)).toString() | ||||
|     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) | ||||
|     if ( | ||||
|       placeholderIndex < 0 || | ||||
|       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) | ||||
|     ) { | ||||
|       throw new Error(`Unable to replace auth placeholder in ${credentialsConfigPath}`) | ||||
|     } | ||||
|     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') | ||||
|     content = content.replace( | ||||
|       this.tokenPlaceholderConfigValue, | ||||
|       this.tokenConfigValue | ||||
|     ) | ||||
|     await fs.promises.writeFile(credentialsConfigPath, content) | ||||
| 
 | ||||
|     // Add include or includeIf to reference the credentials config
 | ||||
|     if (globalConfig) { | ||||
|  | @ -370,7 +369,6 @@ class GitAuthHelper { | |||
|       // Configure host includeIf
 | ||||
|       const hostIncludeKey = `includeIf.gitdir:${gitDir}.path` | ||||
|       await this.git.config(hostIncludeKey, credentialsConfigPath) | ||||
|       this.credentialsIncludeKeys.push(hostIncludeKey) | ||||
| 
 | ||||
|       // Container git directory
 | ||||
|       const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||
|  | @ -393,33 +391,39 @@ class GitAuthHelper { | |||
|       // Configure container includeIf
 | ||||
|       const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path` | ||||
|       await this.git.config(containerIncludeKey, containerCredentialsPath) | ||||
|       this.credentialsIncludeKeys.push(containerIncludeKey) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async replaceTokenPlaceholder(configPath: string): Promise<void> { | ||||
|     assert.ok(configPath, 'configPath is not defined') | ||||
|     let content = (await fs.promises.readFile(configPath)).toString() | ||||
|     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) | ||||
|     if ( | ||||
|       placeholderIndex < 0 || | ||||
|       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) | ||||
|     ) { | ||||
|       throw new Error(`Unable to replace auth placeholder in ${configPath}`) | ||||
|   /** | ||||
|    * Gets or creates the path to the credentials config file in RUNNER_TEMP. | ||||
|    * @returns The absolute path to the credentials config file | ||||
|    */ | ||||
|   private async getCredentialsConfigPath(): Promise<string> { | ||||
|     if (this.credentialsConfigPath) { | ||||
|       return this.credentialsConfigPath | ||||
|     } | ||||
|     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') | ||||
|     content = content.replace( | ||||
|       this.tokenPlaceholderConfigValue, | ||||
|       this.tokenConfigValue | ||||
|     ) | ||||
|     await fs.promises.writeFile(configPath, content) | ||||
| 
 | ||||
|     const runnerTemp = process.env['RUNNER_TEMP'] || '' | ||||
|     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||
| 
 | ||||
|     // Create a unique filename for this checkout instance
 | ||||
|     const configFileName = `git-credentials-${uuid()}.config` | ||||
|     this.credentialsConfigPath = path.join(runnerTemp, configFileName) | ||||
| 
 | ||||
|     core.debug(`Credentials config path: ${this.credentialsConfigPath}`) | ||||
|     return this.credentialsConfigPath | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Removes SSH authentication configuration by cleaning up SSH keys, | ||||
|    * known hosts files, and SSH command configurations. | ||||
|    */ | ||||
|   private async removeSsh(): Promise<void> { | ||||
|     // SSH key
 | ||||
|     const keyPath = this.sshKeyPath || stateHelper.SshKeyPath | ||||
|     if (keyPath) { | ||||
|       try { | ||||
|         core.info(`Removing SSH key '${keyPath}'`) | ||||
|         await io.rmRF(keyPath) | ||||
|       } catch (err) { | ||||
|         core.debug(`${(err as any)?.message ?? err}`) | ||||
|  | @ -432,65 +436,70 @@ class GitAuthHelper { | |||
|       this.sshKnownHostsPath || stateHelper.SshKnownHostsPath | ||||
|     if (knownHostsPath) { | ||||
|       try { | ||||
|         core.info(`Removing SSH known hosts '${knownHostsPath}'`) | ||||
|         await io.rmRF(knownHostsPath) | ||||
|       } catch { | ||||
|         // Intentionally empty
 | ||||
|       } catch (err) { | ||||
|         core.debug(`${(err as any)?.message ?? err}`) | ||||
|         core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // SSH command
 | ||||
|     core.info("Removing SSH command configuration") | ||||
|     await this.removeGitConfig(SSH_COMMAND_KEY) | ||||
|     await this.removeSubmoduleGitConfig(SSH_COMMAND_KEY) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Removes token-based authentication by cleaning up HTTP headers, | ||||
|    * includeIf entries, and credentials config files. | ||||
|    */ | ||||
|   private async removeToken(): Promise<void> { | ||||
|     // Remove HTTP extra header
 | ||||
|     core.info("Removing HTTP extra header") | ||||
|     await this.removeGitConfig(this.tokenConfigKey) | ||||
|     await this.removeSubmoduleGitConfig(this.tokenConfigKey) | ||||
| 
 | ||||
|     // Collect credentials config paths that need to be removed
 | ||||
|     const credentialsPaths = new Set<string>() | ||||
| 
 | ||||
|     // Remove includeIf entries that point to git-credentials-*.config files
 | ||||
|     // This is more aggressive than tracking keys, but necessary since cleanup
 | ||||
|     // runs in a post-step where this.credentialsIncludeKeys is empty
 | ||||
|     try { | ||||
|       // Get all includeIf.gitdir keys
 | ||||
|       const keys = await this.git.tryGetConfigKeys('^includeIf\\.gitdir:') | ||||
|        | ||||
|       for (const key of keys) { | ||||
|         // Get all values for this key
 | ||||
|         const values = await this.git.tryGetConfigValues(key) | ||||
|         if (values.length > 0) { | ||||
|           // Remove only values that match git-credentials-<uuid>.config pattern
 | ||||
|           for (const value of values) { | ||||
|             if (/git-credentials-[0-9a-f-]+\.config$/i.test(value)) { | ||||
|               await this.git.tryConfigUnsetValue(key, value) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } catch (err) { | ||||
|       // Ignore errors - this is cleanup code
 | ||||
|       core.debug(`Error during includeIf cleanup: ${err}`) | ||||
|     core.info("Removing includeIf entries pointing to credentials config files") | ||||
|     const mainCredentialsPaths = await this.removeIncludeIfCredentials() | ||||
|     mainCredentialsPaths.forEach(path => credentialsPaths.add(path)) | ||||
| 
 | ||||
|     // Remove submodule includeIf entries that point to git-credentials-*.config files
 | ||||
|     const submoduleConfigPaths = await this.git.getSubmoduleConfigPaths(true) | ||||
|     for (const configPath of submoduleConfigPaths) { | ||||
|       const submoduleCredentialsPaths = await this.removeIncludeIfCredentials(configPath) | ||||
|       submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path)) | ||||
|     } | ||||
| 
 | ||||
|     // Remove submodule includeIf
 | ||||
|     await this.git.submoduleForeach( | ||||
|       `sh -c "git config --local --get-regexp '^includeIf\\.' && git config --local --remove-section includeIf || :"`, | ||||
|       true | ||||
|     ) | ||||
| 
 | ||||
|     // Remove credentials config file
 | ||||
|     if (this.credentialsConfigPath) { | ||||
|       try { | ||||
|         await io.rmRF(this.credentialsConfigPath) | ||||
|       } catch (err) { | ||||
|         core.debug(`${(err as any)?.message ?? err}`) | ||||
|         core.warning( | ||||
|           `Failed to remove credentials config '${this.credentialsConfigPath}'` | ||||
|         ) | ||||
|     // Remove credentials config files
 | ||||
|     for (const credentialsPath of credentialsPaths) { | ||||
|       // Only remove credentials config files if they are under RUNNER_TEMP
 | ||||
|       const runnerTemp = process.env['RUNNER_TEMP'] | ||||
|       assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||
|       if (credentialsPath.startsWith(runnerTemp)) { | ||||
|         try { | ||||
|           core.info(`Removing credentials config '${credentialsPath}'`) | ||||
|           await io.rmRF(credentialsPath) | ||||
|         } catch (err) { | ||||
|           core.debug(`${(err as any)?.message ?? err}`) | ||||
|           core.warning( | ||||
|             `Failed to remove credentials config '${credentialsPath}'` | ||||
|           ) | ||||
|         } | ||||
|       } else { | ||||
|         core.debug(`Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP`) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Removes a git config key from the local repository config. | ||||
|    * @param configKey The git config key to remove | ||||
|    */ | ||||
|   private async removeGitConfig(configKey: string): Promise<void> { | ||||
|     if ( | ||||
|       (await this.git.configExists(configKey)) && | ||||
|  | @ -501,6 +510,10 @@ class GitAuthHelper { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Removes a git config key from all submodule configs. | ||||
|    * @param configKey The git config key to remove | ||||
|    */ | ||||
|   private async removeSubmoduleGitConfig(configKey: string): Promise<void> { | ||||
|     const pattern = regexpHelper.escape(configKey) | ||||
|     await this.git.submoduleForeach( | ||||
|  | @ -509,4 +522,50 @@ class GitAuthHelper { | |||
|       true | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Removes includeIf entries that point to git-credentials-*.config files. | ||||
|    * @param configPath Optional path to a specific git config file to operate on | ||||
|    * @returns Array of unique credentials config file paths that were found and removed | ||||
|    */ | ||||
|   private async removeIncludeIfCredentials(configPath?: string): Promise<string[]> { | ||||
|     const credentialsPaths = new Set<string>() | ||||
|      | ||||
|     try { | ||||
|       // Get all includeIf.gitdir keys
 | ||||
|       const keys = await this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, configPath) | ||||
|        | ||||
|       for (const key of keys) { | ||||
|         // Get all values for this key
 | ||||
|         const values = await this.git.tryGetConfigValues(key, false, configPath) | ||||
|         if (values.length > 0) { | ||||
|           // Remove only values that match git-credentials-<uuid>.config pattern
 | ||||
|           for (const value of values) { | ||||
|             if (this.testCredentialsConfigPath(value)) { | ||||
|               credentialsPaths.add(value) | ||||
|               await this.git.tryConfigUnsetValue(key, value, false, configPath) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } catch (err) { | ||||
|       // Ignore errors - this is cleanup code
 | ||||
|       if (configPath) { | ||||
|         core.debug(`Error during includeIf cleanup for ${configPath}: ${err}`) | ||||
|       } else { | ||||
|         core.debug(`Error during includeIf cleanup: ${err}`) | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     return Array.from(credentialsPaths) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests if a path matches the git-credentials-*.config pattern. | ||||
|    * @param path The path to test | ||||
|    * @returns True if the path matches the credentials config pattern | ||||
|    */ | ||||
|   private testCredentialsConfigPath(path: string): boolean { | ||||
|     return /git-credentials-[0-9a-f-]+\.config$/i.test(path) | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ export interface IGitCommandManager { | |||
|     } | ||||
|   ): Promise<void> | ||||
|   getDefaultBranch(repositoryUrl: string): Promise<string> | ||||
|   getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> | ||||
|   getWorkingDirectory(): string | ||||
|   init(): Promise<void> | ||||
|   isDetached(): Promise<boolean> | ||||
|  | @ -60,11 +61,11 @@ export interface IGitCommandManager { | |||
|   tagExists(pattern: string): Promise<boolean> | ||||
|   tryClean(): Promise<boolean> | ||||
|   tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean> | ||||
|   tryConfigUnsetValue(configKey: string, configValue: string, globalConfig?: boolean): Promise<boolean> | ||||
|   tryConfigUnsetValue(configKey: string, configValue: string, globalConfig?: boolean, configFile?: string): Promise<boolean> | ||||
|   tryDisableAutomaticGarbageCollection(): Promise<boolean> | ||||
|   tryGetFetchUrl(): Promise<string> | ||||
|   tryGetConfigValues(configKey: string, globalConfig?: boolean): Promise<string[]> | ||||
|   tryGetConfigKeys(pattern: string, globalConfig?: boolean): Promise<string[]> | ||||
|   tryGetConfigValues(configKey: string, globalConfig?: boolean, configFile?: string): Promise<string[]> | ||||
|   tryGetConfigKeys(pattern: string, globalConfig?: boolean, configFile?: string): Promise<string[]> | ||||
|   tryReset(): Promise<boolean> | ||||
|   version(): Promise<GitVersion> | ||||
| } | ||||
|  | @ -333,6 +334,21 @@ class GitCommandManager { | |||
|     throw new Error('Unexpected output when retrieving default branch') | ||||
|   } | ||||
| 
 | ||||
|   async getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> { | ||||
|     // Get submodule config file paths.
 | ||||
|     // Use `--show-origin` to get the config file path for each submodule.
 | ||||
|     const output = await this.submoduleForeach( | ||||
|       `git config --local --show-origin --name-only --get-regexp remote.origin.url`, | ||||
|       recursive | ||||
|     ) | ||||
| 
 | ||||
|     // Extract config file paths from the output (lines starting with "file:").
 | ||||
|     const configPaths = | ||||
|       output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] | ||||
| 
 | ||||
|     return configPaths | ||||
|   } | ||||
| 
 | ||||
|   getWorkingDirectory(): string { | ||||
|     return this.workingDirectory | ||||
|   } | ||||
|  | @ -468,18 +484,18 @@ class GitCommandManager { | |||
|   async tryConfigUnsetValue( | ||||
|     configKey: string, | ||||
|     configValue: string, | ||||
|     globalConfig?: boolean | ||||
|     globalConfig?: boolean, | ||||
|     configFile?: string | ||||
|   ): Promise<boolean> { | ||||
|     const output = await this.execGit( | ||||
|       [ | ||||
|         'config', | ||||
|         globalConfig ? '--global' : '--local', | ||||
|         '--unset', | ||||
|         configKey, | ||||
|         configValue | ||||
|       ], | ||||
|       true | ||||
|     ) | ||||
|     const args = ['config'] | ||||
|     if (configFile) { | ||||
|       args.push('--file', configFile) | ||||
|     } else { | ||||
|       args.push(globalConfig ? '--global' : '--local') | ||||
|     } | ||||
|     args.push('--unset', configKey, configValue) | ||||
|      | ||||
|     const output = await this.execGit(args, true) | ||||
|     return output.exitCode === 0 | ||||
|   } | ||||
| 
 | ||||
|  | @ -511,17 +527,18 @@ class GitCommandManager { | |||
| 
 | ||||
|   async tryGetConfigValues( | ||||
|     configKey: string, | ||||
|     globalConfig?: boolean | ||||
|     globalConfig?: boolean, | ||||
|     configFile?: string | ||||
|   ): Promise<string[]> { | ||||
|     const output = await this.execGit( | ||||
|       [ | ||||
|         'config', | ||||
|         globalConfig ? '--global' : '--local', | ||||
|         '--get-all', | ||||
|         configKey | ||||
|       ], | ||||
|       true | ||||
|     ) | ||||
|     const args = ['config'] | ||||
|     if (configFile) { | ||||
|       args.push('--file', configFile) | ||||
|     } else { | ||||
|       args.push(globalConfig ? '--global' : '--local') | ||||
|     } | ||||
|     args.push('--get-all', configKey) | ||||
|      | ||||
|     const output = await this.execGit(args, true) | ||||
| 
 | ||||
|     if (output.exitCode !== 0) { | ||||
|       return [] | ||||
|  | @ -532,18 +549,18 @@ class GitCommandManager { | |||
| 
 | ||||
|   async tryGetConfigKeys( | ||||
|     pattern: string, | ||||
|     globalConfig?: boolean | ||||
|     globalConfig?: boolean, | ||||
|     configFile?: string | ||||
|   ): Promise<string[]> { | ||||
|     const output = await this.execGit( | ||||
|       [ | ||||
|         'config', | ||||
|         globalConfig ? '--global' : '--local', | ||||
|         '--name-only', | ||||
|         '--get-regexp', | ||||
|         pattern | ||||
|       ], | ||||
|       true | ||||
|     ) | ||||
|     const args = ['config'] | ||||
|     if (configFile) { | ||||
|       args.push('--file', configFile) | ||||
|     } else { | ||||
|       args.push(globalConfig ? '--global' : '--local') | ||||
|     } | ||||
|     args.push('--name-only', '--get-regexp', pattern) | ||||
|      | ||||
|     const output = await this.execGit(args, true) | ||||
| 
 | ||||
|     if (output.exitCode !== 0) { | ||||
|       return [] | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user