mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-31 10:24:20 +08:00 
			
		
		
		
	.
This commit is contained in:
		
							parent
							
								
									3292e202f3
								
							
						
					
					
						commit
						f8060825ea
					
				| @ -595,18 +595,15 @@ describe('git-auth-helper tests', () => { | |||||||
|       await authHelper.configureSubmoduleAuth() |       await authHelper.configureSubmoduleAuth() | ||||||
| 
 | 
 | ||||||
|       // Assert
 |       // Assert
 | ||||||
|       // Should get submodule config paths (1 call) and configure insteadOf (2 calls for two values)
 |       // Should configure insteadOf (2 calls for two values)
 | ||||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4) |       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( |       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch( | ||||||
|         /show-origin.*remote\.origin\.url/ |  | ||||||
|       ) |  | ||||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( |  | ||||||
|         /url.*insteadOf.*git@github.com:/ |         /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:/ |         /url.*insteadOf.*org-123456@github.com:/ | ||||||
|       ) |       ) | ||||||
|     } |     } | ||||||
| @ -637,15 +634,12 @@ describe('git-auth-helper tests', () => { | |||||||
|       await authHelper.configureSubmoduleAuth() |       await authHelper.configureSubmoduleAuth() | ||||||
| 
 | 
 | ||||||
|       // Assert
 |       // Assert
 | ||||||
|       // Should get submodule config paths (1 call) and configure sshCommand (1 call)
 |       // Should configure sshCommand (1 call)
 | ||||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) |       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2) | ||||||
|       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( |       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/core\.sshCommand/) | ||||||
|         /show-origin.*remote\.origin\.url/ |  | ||||||
|       ) |  | ||||||
|       expect(mockSubmoduleForeach.mock.calls[2][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> { | async function setup(testName: string): Promise<void> { | ||||||
| @ -834,6 +850,7 @@ async function setup(testName: string): Promise<void> { | |||||||
|     env: {}, |     env: {}, | ||||||
|     fetch: jest.fn(), |     fetch: jest.fn(), | ||||||
|     getDefaultBranch: jest.fn(), |     getDefaultBranch: jest.fn(), | ||||||
|  |     getSubmoduleConfigPaths: jest.fn(async () => []), | ||||||
|     getWorkingDirectory: jest.fn(() => workspace), |     getWorkingDirectory: jest.fn(() => workspace), | ||||||
|     init: jest.fn(), |     init: jest.fn(), | ||||||
|     isDetached: jest.fn(), |     isDetached: jest.fn(), | ||||||
|  | |||||||
| @ -471,6 +471,7 @@ async function setup(testName: string): Promise<void> { | |||||||
|     configExists: jest.fn(), |     configExists: jest.fn(), | ||||||
|     fetch: jest.fn(), |     fetch: jest.fn(), | ||||||
|     getDefaultBranch: jest.fn(), |     getDefaultBranch: jest.fn(), | ||||||
|  |     getSubmoduleConfigPaths: jest.fn(async () => []), | ||||||
|     getWorkingDirectory: jest.fn(() => repositoryPath), |     getWorkingDirectory: jest.fn(() => repositoryPath), | ||||||
|     init: jest.fn(), |     init: jest.fn(), | ||||||
|     isDetached: jest.fn(), |     isDetached: jest.fn(), | ||||||
|  | |||||||
							
								
								
									
										213
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										213
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -163,7 +163,6 @@ class GitAuthHelper { | |||||||
|         this.sshKnownHostsPath = ''; |         this.sshKnownHostsPath = ''; | ||||||
|         this.temporaryHomePath = ''; |         this.temporaryHomePath = ''; | ||||||
|         this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
 |         this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
 | ||||||
|         this.credentialsIncludeKeys = []; // Track includeIf config keys for cleanup
 |  | ||||||
|         this.git = gitCommandManager; |         this.git = gitCommandManager; | ||||||
|         this.settings = gitSourceSettings || {}; |         this.settings = gitSourceSettings || {}; | ||||||
|         // Token auth header
 |         // Token auth header
 | ||||||
| @ -189,20 +188,6 @@ class GitAuthHelper { | |||||||
|             yield this.configureToken(); |             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() { |     configureTempGlobalConfig() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             var _a; |             var _a; | ||||||
| @ -282,10 +267,7 @@ class GitAuthHelper { | |||||||
|                 relativePath = relativePath.replace(/\\/g, '/'); |                 relativePath = relativePath.replace(/\\/g, '/'); | ||||||
|                 const containerRepoPath = path.posix.join('/github/workspace', relativePath); |                 const containerRepoPath = path.posix.join('/github/workspace', relativePath); | ||||||
|                 // Get submodule config file paths.
 |                 // Get submodule config file paths.
 | ||||||
|                 // Use `--show-origin` to get the config file path for each submodule.
 |                 const configPaths = yield this.git.getSubmoduleConfigPaths(this.settings.nestedSubmodules); | ||||||
|                 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) || []; |  | ||||||
|                 // For each submodule, configure includeIf entries pointing to the shared credentials file.
 |                 // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | ||||||
|                 // Configure both host and container paths to support Docker container actions.
 |                 // Configure both host and container paths to support Docker container actions.
 | ||||||
|                 for (const configPath of configPaths) { |                 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() { |     configureSsh() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             if (!this.settings.sshKey) { |             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) { |     configureToken(globalConfig) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // Get the credentials config file path in RUNNER_TEMP
 |             // 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
 |             // 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); |             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, false, credentialsConfigPath); | ||||||
|             // Replace the placeholder in the credentials config file
 |             // 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
 |             // Add include or includeIf to reference the credentials config
 | ||||||
|             if (globalConfig) { |             if (globalConfig) { | ||||||
|                 // Global config file is temporary
 |                 // Global config file is temporary
 | ||||||
| @ -408,7 +407,6 @@ class GitAuthHelper { | |||||||
|                 // Configure host includeIf
 |                 // Configure host includeIf
 | ||||||
|                 const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`; |                 const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`; | ||||||
|                 yield this.git.config(hostIncludeKey, credentialsConfigPath); |                 yield this.git.config(hostIncludeKey, credentialsConfigPath); | ||||||
|                 this.credentialsIncludeKeys.push(hostIncludeKey); |  | ||||||
|                 // Container git directory
 |                 // Container git directory
 | ||||||
|                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; |                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||||
|                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); |                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||||
| @ -421,24 +419,31 @@ class GitAuthHelper { | |||||||
|                 // Configure container includeIf
 |                 // Configure container includeIf
 | ||||||
|                 const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`; |                 const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`; | ||||||
|                 yield this.git.config(containerIncludeKey, containerCredentialsPath); |                 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* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             assert.ok(configPath, 'configPath is not defined'); |             if (this.credentialsConfigPath) { | ||||||
|             let content = (yield fs.promises.readFile(configPath)).toString(); |                 return this.credentialsConfigPath; | ||||||
|             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); |  | ||||||
|             if (placeholderIndex < 0 || |  | ||||||
|                 placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { |  | ||||||
|                 throw new Error(`Unable to replace auth placeholder in ${configPath}`); |  | ||||||
|             } |             } | ||||||
|             assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined'); |             const runnerTemp = process.env['RUNNER_TEMP'] || ''; | ||||||
|             content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue); |             assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||||
|             yield fs.promises.writeFile(configPath, content); |             // 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() { |     removeSsh() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             var _a; |             var _a; | ||||||
| @ -468,6 +473,10 @@ class GitAuthHelper { | |||||||
|             yield this.removeSubmoduleGitConfig(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() { |     removeToken() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             var _a; |             var _a; | ||||||
| @ -475,30 +484,12 @@ class GitAuthHelper { | |||||||
|             yield this.removeGitConfig(this.tokenConfigKey); |             yield this.removeGitConfig(this.tokenConfigKey); | ||||||
|             yield this.removeSubmoduleGitConfig(this.tokenConfigKey); |             yield this.removeSubmoduleGitConfig(this.tokenConfigKey); | ||||||
|             // Remove includeIf entries that point to git-credentials-*.config files
 |             // Remove includeIf entries that point to git-credentials-*.config files
 | ||||||
|             // This is more aggressive than tracking keys, but necessary since cleanup
 |             yield this.removeIncludeIfCredentials(); | ||||||
|             // runs in a post-step where this.credentialsIncludeKeys is empty
 |             // Remove submodule includeIf entries that point to git-credentials-*.config files
 | ||||||
|             try { |             const submoduleConfigPaths = yield this.git.getSubmoduleConfigPaths(true); | ||||||
|                 // Get all includeIf.gitdir keys
 |             for (const configPath of submoduleConfigPaths) { | ||||||
|                 const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:'); |                 yield this.removeIncludeIfCredentials(configPath); | ||||||
|                 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); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             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
 |             // Remove credentials config file
 | ||||||
|             if (this.credentialsConfigPath) { |             if (this.credentialsConfigPath) { | ||||||
|                 try { |                 try { | ||||||
| @ -511,6 +502,10 @@ class GitAuthHelper { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     /** | ||||||
|  |      * Removes a git config key from the local repository config. | ||||||
|  |      * @param configKey The git config key to remove | ||||||
|  |      */ | ||||||
|     removeGitConfig(configKey) { |     removeGitConfig(configKey) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             if ((yield this.git.configExists(configKey)) && |             if ((yield this.git.configExists(configKey)) && | ||||||
| @ -520,6 +515,10 @@ class GitAuthHelper { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     /** | ||||||
|  |      * Removes a git config key from all submodule configs. | ||||||
|  |      * @param configKey The git config key to remove | ||||||
|  |      */ | ||||||
|     removeSubmoduleGitConfig(configKey) { |     removeSubmoduleGitConfig(configKey) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const pattern = regexpHelper.escape(configKey); |             const pattern = regexpHelper.escape(configKey); | ||||||
| @ -528,6 +527,47 @@ class GitAuthHelper { | |||||||
|             `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true); |             `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 | ||||||
|  |      */ | ||||||
|  |     removeIncludeIfCredentials(configPath) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             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)) { | ||||||
|  |                                 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}`); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * 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 +850,16 @@ class GitCommandManager { | |||||||
|             throw new Error('Unexpected output when retrieving default branch'); |             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() { |     getWorkingDirectory() { | ||||||
|         return this.workingDirectory; |         return this.workingDirectory; | ||||||
|     } |     } | ||||||
| @ -940,15 +990,17 @@ class GitCommandManager { | |||||||
|             return output.exitCode === 0; |             return output.exitCode === 0; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     tryConfigUnsetValue(configKey, configValue, globalConfig) { |     tryConfigUnsetValue(configKey, configValue, globalConfig, configFile) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const output = yield this.execGit([ |             const args = ['config']; | ||||||
|                 'config', |             if (configFile) { | ||||||
|                 globalConfig ? '--global' : '--local', |                 args.push('--file', configFile); | ||||||
|                 '--unset', |             } | ||||||
|                 configKey, |             else { | ||||||
|                 configValue |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|             ], true); |             } | ||||||
|  |             args.push('--unset', configKey, configValue); | ||||||
|  |             const output = yield this.execGit(args, true); | ||||||
|             return output.exitCode === 0; |             return output.exitCode === 0; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -971,29 +1023,34 @@ class GitCommandManager { | |||||||
|             return stdout; |             return stdout; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     tryGetConfigValues(configKey, globalConfig) { |     tryGetConfigValues(configKey, globalConfig, configFile) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const output = yield this.execGit([ |             const args = ['config']; | ||||||
|                 'config', |             if (configFile) { | ||||||
|                 globalConfig ? '--global' : '--local', |                 args.push('--file', configFile); | ||||||
|                 '--get-all', |             } | ||||||
|                 configKey |             else { | ||||||
|             ], true); |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|  |             } | ||||||
|  |             args.push('--get-all', configKey); | ||||||
|  |             const output = yield this.execGit(args, true); | ||||||
|             if (output.exitCode !== 0) { |             if (output.exitCode !== 0) { | ||||||
|                 return []; |                 return []; | ||||||
|             } |             } | ||||||
|             return output.stdout.trim().split('\n').filter(value => value.trim()); |             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* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const output = yield this.execGit([ |             const args = ['config']; | ||||||
|                 'config', |             if (configFile) { | ||||||
|                 globalConfig ? '--global' : '--local', |                 args.push('--file', configFile); | ||||||
|                 '--name-only', |             } | ||||||
|                 '--get-regexp', |             else { | ||||||
|                 pattern |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|             ], true); |             } | ||||||
|  |             args.push('--name-only', '--get-regexp', pattern); | ||||||
|  |             const output = yield this.execGit(args, true); | ||||||
|             if (output.exitCode !== 0) { |             if (output.exitCode !== 0) { | ||||||
|                 return []; |                 return []; | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -44,7 +44,6 @@ class GitAuthHelper { | |||||||
|   private sshKnownHostsPath = '' |   private sshKnownHostsPath = '' | ||||||
|   private temporaryHomePath = '' |   private temporaryHomePath = '' | ||||||
|   private credentialsConfigPath = '' // Path to separate credentials config file in RUNNER_TEMP
 |   private credentialsConfigPath = '' // Path to separate credentials config file in RUNNER_TEMP
 | ||||||
|   private credentialsIncludeKeys: string[] = [] // Track includeIf config keys for cleanup
 |  | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     gitCommandManager: IGitCommandManager, |     gitCommandManager: IGitCommandManager, | ||||||
| @ -83,22 +82,6 @@ class GitAuthHelper { | |||||||
|     await this.configureToken() |     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> { |   async configureTempGlobalConfig(): Promise<string> { | ||||||
|     // Already setup global config
 |     // Already setup global config
 | ||||||
|     if (this.temporaryHomePath?.length > 0) { |     if (this.temporaryHomePath?.length > 0) { | ||||||
| @ -192,16 +175,10 @@ class GitAuthHelper { | |||||||
|       ) |       ) | ||||||
| 
 | 
 | ||||||
|       // Get submodule config file paths.
 |       // Get submodule config file paths.
 | ||||||
|       // Use `--show-origin` to get the config file path for each submodule.
 |       const configPaths = await this.git.getSubmoduleConfigPaths( | ||||||
|       const output = await this.git.submoduleForeach( |  | ||||||
|         `git config --local --show-origin --name-only --get-regexp remote.origin.url`, |  | ||||||
|         this.settings.nestedSubmodules |         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.
 |       // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | ||||||
|       // Configure both host and container paths to support Docker container actions.
 |       // Configure both host and container paths to support Docker container actions.
 | ||||||
|       for (const configPath of configPaths) { |       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> { |   private async configureSsh(): Promise<void> { | ||||||
|     if (!this.settings.sshKey) { |     if (!this.settings.sshKey) { | ||||||
|       return |       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> { |   private async configureToken(globalConfig?: boolean): Promise<void> { | ||||||
|     // Get the credentials config file path in RUNNER_TEMP
 |     // Get the credentials config file path in RUNNER_TEMP
 | ||||||
|     const credentialsConfigPath = await this.getCredentialsConfigPath() |     const credentialsConfigPath = await this.getCredentialsConfigPath() | ||||||
| @ -356,7 +342,20 @@ class GitAuthHelper { | |||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     // Replace the placeholder in the credentials config file
 |     // 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
 |     // Add include or includeIf to reference the credentials config
 | ||||||
|     if (globalConfig) { |     if (globalConfig) { | ||||||
| @ -370,7 +369,6 @@ class GitAuthHelper { | |||||||
|       // Configure host includeIf
 |       // Configure host includeIf
 | ||||||
|       const hostIncludeKey = `includeIf.gitdir:${gitDir}.path` |       const hostIncludeKey = `includeIf.gitdir:${gitDir}.path` | ||||||
|       await this.git.config(hostIncludeKey, credentialsConfigPath) |       await this.git.config(hostIncludeKey, credentialsConfigPath) | ||||||
|       this.credentialsIncludeKeys.push(hostIncludeKey) |  | ||||||
| 
 | 
 | ||||||
|       // Container git directory
 |       // Container git directory
 | ||||||
|       const githubWorkspace = process.env['GITHUB_WORKSPACE'] |       const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||||
| @ -393,28 +391,33 @@ class GitAuthHelper { | |||||||
|       // Configure container includeIf
 |       // Configure container includeIf
 | ||||||
|       const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path` |       const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path` | ||||||
|       await this.git.config(containerIncludeKey, containerCredentialsPath) |       await this.git.config(containerIncludeKey, containerCredentialsPath) | ||||||
|       this.credentialsIncludeKeys.push(containerIncludeKey) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async replaceTokenPlaceholder(configPath: string): Promise<void> { |   /** | ||||||
|     assert.ok(configPath, 'configPath is not defined') |    * Gets or creates the path to the credentials config file in RUNNER_TEMP. | ||||||
|     let content = (await fs.promises.readFile(configPath)).toString() |    * @returns The absolute path to the credentials config file | ||||||
|     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) |    */ | ||||||
|     if ( |   private async getCredentialsConfigPath(): Promise<string> { | ||||||
|       placeholderIndex < 0 || |     if (this.credentialsConfigPath) { | ||||||
|       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) |       return this.credentialsConfigPath | ||||||
|     ) { |  | ||||||
|       throw new Error(`Unable to replace auth placeholder in ${configPath}`) |  | ||||||
|     } |     } | ||||||
|     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') | 
 | ||||||
|     content = content.replace( |     const runnerTemp = process.env['RUNNER_TEMP'] || '' | ||||||
|       this.tokenPlaceholderConfigValue, |     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||||
|       this.tokenConfigValue | 
 | ||||||
|     ) |     // Create a unique filename for this checkout instance
 | ||||||
|     await fs.promises.writeFile(configPath, content) |     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> { |   private async removeSsh(): Promise<void> { | ||||||
|     // SSH key
 |     // SSH key
 | ||||||
|     const keyPath = this.sshKeyPath || stateHelper.SshKeyPath |     const keyPath = this.sshKeyPath || stateHelper.SshKeyPath | ||||||
| @ -443,41 +446,24 @@ class GitAuthHelper { | |||||||
|     await this.removeSubmoduleGitConfig(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> { |   private async removeToken(): Promise<void> { | ||||||
|     // Remove HTTP extra header
 |     // Remove HTTP extra header
 | ||||||
|     await this.removeGitConfig(this.tokenConfigKey) |     await this.removeGitConfig(this.tokenConfigKey) | ||||||
|     await this.removeSubmoduleGitConfig(this.tokenConfigKey) |     await this.removeSubmoduleGitConfig(this.tokenConfigKey) | ||||||
| 
 | 
 | ||||||
|     // Remove includeIf entries that point to git-credentials-*.config files
 |     // Remove includeIf entries that point to git-credentials-*.config files
 | ||||||
|     // This is more aggressive than tracking keys, but necessary since cleanup
 |     await this.removeIncludeIfCredentials() | ||||||
|     // 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) { |     // Remove submodule includeIf entries that point to git-credentials-*.config files
 | ||||||
|         // Get all values for this key
 |     const submoduleConfigPaths = await this.git.getSubmoduleConfigPaths(true) | ||||||
|         const values = await this.git.tryGetConfigValues(key) |     for (const configPath of submoduleConfigPaths) { | ||||||
|         if (values.length > 0) { |       await this.removeIncludeIfCredentials(configPath) | ||||||
|           // 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}`) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 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
 |     // Remove credentials config file
 | ||||||
|     if (this.credentialsConfigPath) { |     if (this.credentialsConfigPath) { | ||||||
|       try { |       try { | ||||||
| @ -491,6 +477,10 @@ class GitAuthHelper { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** | ||||||
|  |    * 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> { |   private async removeGitConfig(configKey: string): Promise<void> { | ||||||
|     if ( |     if ( | ||||||
|       (await this.git.configExists(configKey)) && |       (await this.git.configExists(configKey)) && | ||||||
| @ -501,6 +491,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> { |   private async removeSubmoduleGitConfig(configKey: string): Promise<void> { | ||||||
|     const pattern = regexpHelper.escape(configKey) |     const pattern = regexpHelper.escape(configKey) | ||||||
|     await this.git.submoduleForeach( |     await this.git.submoduleForeach( | ||||||
| @ -509,4 +503,44 @@ class GitAuthHelper { | |||||||
|       true |       true | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Removes includeIf entries that point to git-credentials-*.config files. | ||||||
|  |    * @param configPath Optional path to a specific git config file to operate on | ||||||
|  |    */ | ||||||
|  |   private async removeIncludeIfCredentials(configPath?: string): Promise<void> { | ||||||
|  |     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)) { | ||||||
|  |               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}`) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 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> |   ): Promise<void> | ||||||
|   getDefaultBranch(repositoryUrl: string): Promise<string> |   getDefaultBranch(repositoryUrl: string): Promise<string> | ||||||
|  |   getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> | ||||||
|   getWorkingDirectory(): string |   getWorkingDirectory(): string | ||||||
|   init(): Promise<void> |   init(): Promise<void> | ||||||
|   isDetached(): Promise<boolean> |   isDetached(): Promise<boolean> | ||||||
| @ -60,11 +61,11 @@ export interface IGitCommandManager { | |||||||
|   tagExists(pattern: string): Promise<boolean> |   tagExists(pattern: string): Promise<boolean> | ||||||
|   tryClean(): Promise<boolean> |   tryClean(): Promise<boolean> | ||||||
|   tryConfigUnset(configKey: string, globalConfig?: boolean): 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> |   tryDisableAutomaticGarbageCollection(): Promise<boolean> | ||||||
|   tryGetFetchUrl(): Promise<string> |   tryGetFetchUrl(): Promise<string> | ||||||
|   tryGetConfigValues(configKey: string, globalConfig?: boolean): Promise<string[]> |   tryGetConfigValues(configKey: string, globalConfig?: boolean, configFile?: string): Promise<string[]> | ||||||
|   tryGetConfigKeys(pattern: string, globalConfig?: boolean): Promise<string[]> |   tryGetConfigKeys(pattern: string, globalConfig?: boolean, configFile?: string): Promise<string[]> | ||||||
|   tryReset(): Promise<boolean> |   tryReset(): Promise<boolean> | ||||||
|   version(): Promise<GitVersion> |   version(): Promise<GitVersion> | ||||||
| } | } | ||||||
| @ -333,6 +334,21 @@ class GitCommandManager { | |||||||
|     throw new Error('Unexpected output when retrieving default branch') |     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 { |   getWorkingDirectory(): string { | ||||||
|     return this.workingDirectory |     return this.workingDirectory | ||||||
|   } |   } | ||||||
| @ -468,18 +484,18 @@ class GitCommandManager { | |||||||
|   async tryConfigUnsetValue( |   async tryConfigUnsetValue( | ||||||
|     configKey: string, |     configKey: string, | ||||||
|     configValue: string, |     configValue: string, | ||||||
|     globalConfig?: boolean |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|   ): Promise<boolean> { |   ): Promise<boolean> { | ||||||
|     const output = await this.execGit( |     const args = ['config'] | ||||||
|       [ |     if (configFile) { | ||||||
|         'config', |       args.push('--file', configFile) | ||||||
|         globalConfig ? '--global' : '--local', |     } else { | ||||||
|         '--unset', |       args.push(globalConfig ? '--global' : '--local') | ||||||
|         configKey, |     } | ||||||
|         configValue |     args.push('--unset', configKey, configValue) | ||||||
|       ], |      | ||||||
|       true |     const output = await this.execGit(args, true) | ||||||
|     ) |  | ||||||
|     return output.exitCode === 0 |     return output.exitCode === 0 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -511,17 +527,18 @@ class GitCommandManager { | |||||||
| 
 | 
 | ||||||
|   async tryGetConfigValues( |   async tryGetConfigValues( | ||||||
|     configKey: string, |     configKey: string, | ||||||
|     globalConfig?: boolean |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|   ): Promise<string[]> { |   ): Promise<string[]> { | ||||||
|     const output = await this.execGit( |     const args = ['config'] | ||||||
|       [ |     if (configFile) { | ||||||
|         'config', |       args.push('--file', configFile) | ||||||
|         globalConfig ? '--global' : '--local', |     } else { | ||||||
|         '--get-all', |       args.push(globalConfig ? '--global' : '--local') | ||||||
|         configKey |     } | ||||||
|       ], |     args.push('--get-all', configKey) | ||||||
|       true |      | ||||||
|     ) |     const output = await this.execGit(args, true) | ||||||
| 
 | 
 | ||||||
|     if (output.exitCode !== 0) { |     if (output.exitCode !== 0) { | ||||||
|       return [] |       return [] | ||||||
| @ -532,18 +549,18 @@ class GitCommandManager { | |||||||
| 
 | 
 | ||||||
|   async tryGetConfigKeys( |   async tryGetConfigKeys( | ||||||
|     pattern: string, |     pattern: string, | ||||||
|     globalConfig?: boolean |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|   ): Promise<string[]> { |   ): Promise<string[]> { | ||||||
|     const output = await this.execGit( |     const args = ['config'] | ||||||
|       [ |     if (configFile) { | ||||||
|         'config', |       args.push('--file', configFile) | ||||||
|         globalConfig ? '--global' : '--local', |     } else { | ||||||
|         '--name-only', |       args.push(globalConfig ? '--global' : '--local') | ||||||
|         '--get-regexp', |     } | ||||||
|         pattern |     args.push('--name-only', '--get-regexp', pattern) | ||||||
|       ], |      | ||||||
|       true |     const output = await this.execGit(args, true) | ||||||
|     ) |  | ||||||
| 
 | 
 | ||||||
|     if (output.exitCode !== 0) { |     if (output.exitCode !== 0) { | ||||||
|       return [] |       return [] | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 eric sciple
						eric sciple