mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-30 23:10:31 +00:00 
			
		
		
		
	Persist creds to a separate file
This commit is contained in:
		| @@ -86,16 +86,29 @@ describe('git-auth-helper tests', () => { | ||||
|     // Act | ||||
|     await authHelper.configureAuth() | ||||
|  | ||||
|     // Assert config | ||||
|     const configContent = ( | ||||
|     // Assert config - check that .git/config contains includeIf entries | ||||
|     const localConfigContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     expect( | ||||
|       localConfigContent.indexOf('includeIf.gitdir:') | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Assert credentials config file contains the actual credentials | ||||
|     const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(1) | ||||
|     const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||
|     const credentialsContent = ( | ||||
|       await fs.promises.readFile(credentialsConfigPath) | ||||
|     ).toString() | ||||
|     const basicCredential = Buffer.from( | ||||
|       `x-access-token:${settings.authToken}`, | ||||
|       'utf8' | ||||
|     ).toString('base64') | ||||
|     expect( | ||||
|       configContent.indexOf( | ||||
|       credentialsContent.indexOf( | ||||
|         `http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
| @@ -120,7 +133,7 @@ describe('git-auth-helper tests', () => { | ||||
|     'inject https://github.com as github server url' | ||||
|   it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => { | ||||
|     await testAuthHeader( | ||||
|       configureAuth_AcceptsGitHubServerUrl, | ||||
|       configureAuth_AcceptsGitHubServerUrlSetToGHEC, | ||||
|       'https://github.com' | ||||
|     ) | ||||
|   }) | ||||
| @@ -141,12 +154,17 @@ describe('git-auth-helper tests', () => { | ||||
|       // Act | ||||
|       await authHelper.configureAuth() | ||||
|  | ||||
|       // Assert config | ||||
|       const configContent = ( | ||||
|         await fs.promises.readFile(localGitConfigPath) | ||||
|       // Assert config - check credentials config file (not local .git/config) | ||||
|       const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|         f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|       ) | ||||
|       expect(credentialsFiles.length).toBe(1) | ||||
|       const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||
|       const credentialsContent = ( | ||||
|         await fs.promises.readFile(credentialsConfigPath) | ||||
|       ).toString() | ||||
|       expect( | ||||
|         configContent.indexOf( | ||||
|         credentialsContent.indexOf( | ||||
|           `http.https://github.com/.extraheader AUTHORIZATION` | ||||
|         ) | ||||
|       ).toBeGreaterThanOrEqual(0) | ||||
| @@ -251,13 +269,16 @@ describe('git-auth-helper tests', () => { | ||||
|       expectedSshCommand | ||||
|     ) | ||||
|  | ||||
|     // Asserty git config | ||||
|     // Assert git config | ||||
|     const gitConfigLines = (await fs.promises.readFile(localGitConfigPath)) | ||||
|       .toString() | ||||
|       .split('\n') | ||||
|       .filter(x => x) | ||||
|     expect(gitConfigLines).toHaveLength(1) | ||||
|     expect(gitConfigLines[0]).toMatch(/^http\./) | ||||
|     // Should have includeIf entries pointing to credentials file | ||||
|     expect(gitConfigLines.length).toBeGreaterThan(0) | ||||
|     expect( | ||||
|       gitConfigLines.some(line => line.indexOf('includeIf.gitdir:') >= 0) | ||||
|     ).toBeTruthy() | ||||
|   }) | ||||
|  | ||||
|   const configureAuth_setsSshCommandWhenPersistCredentialsTrue = | ||||
| @@ -419,8 +440,20 @@ describe('git-auth-helper tests', () => { | ||||
|     expect( | ||||
|       configContent.indexOf('value-from-global-config') | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     // Global config should have include.path pointing to credentials file | ||||
|     expect(configContent.indexOf('include.path')).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Check credentials in the separate config file | ||||
|     const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBeGreaterThan(0) | ||||
|     const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||
|     const credentialsContent = ( | ||||
|       await fs.promises.readFile(credentialsConfigPath) | ||||
|     ).toString() | ||||
|     expect( | ||||
|       configContent.indexOf( | ||||
|       credentialsContent.indexOf( | ||||
|         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
| @@ -463,8 +496,20 @@ describe('git-auth-helper tests', () => { | ||||
|       const configContent = ( | ||||
|         await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig')) | ||||
|       ).toString() | ||||
|       // Global config should have include.path pointing to credentials file | ||||
|       expect(configContent.indexOf('include.path')).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|       // Check credentials in the separate config file | ||||
|       const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|         f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|       ) | ||||
|       expect(credentialsFiles.length).toBeGreaterThan(0) | ||||
|       const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||
|       const credentialsContent = ( | ||||
|         await fs.promises.readFile(credentialsConfigPath) | ||||
|       ).toString() | ||||
|       expect( | ||||
|         configContent.indexOf( | ||||
|         credentialsContent.indexOf( | ||||
|           `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|         ) | ||||
|       ).toBeGreaterThanOrEqual(0) | ||||
| @@ -550,15 +595,15 @@ describe('git-auth-helper tests', () => { | ||||
|       await authHelper.configureSubmoduleAuth() | ||||
|  | ||||
|       // Assert | ||||
|       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(/http.*extraheader/) | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][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:/ | ||||
|       ) | ||||
|     } | ||||
| @@ -589,12 +634,12 @@ describe('git-auth-helper tests', () => { | ||||
|       await authHelper.configureSubmoduleAuth() | ||||
|  | ||||
|       // Assert | ||||
|       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(/http.*extraheader/) | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/core\.sshCommand/) | ||||
|     } | ||||
|   ) | ||||
|  | ||||
| @@ -660,19 +705,35 @@ describe('git-auth-helper tests', () => { | ||||
|     await setup(removeAuth_removesToken) | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     await authHelper.configureAuth() | ||||
|     let gitConfigContent = ( | ||||
|  | ||||
|     // Sanity check - verify includeIf entries exist in local config | ||||
|     let localConfigContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     expect(gitConfigContent.indexOf('http.')).toBeGreaterThanOrEqual(0) // sanity check | ||||
|     expect( | ||||
|       localConfigContent.indexOf('includeIf.gitdir:') | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|  | ||||
|     // Sanity check - verify credentials file exists | ||||
|     let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(1) | ||||
|  | ||||
|     // Act | ||||
|     await authHelper.removeAuth() | ||||
|  | ||||
|     // Assert git config | ||||
|     gitConfigContent = ( | ||||
|     // Assert includeIf entries removed from local git config | ||||
|     localConfigContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     expect(gitConfigContent.indexOf('http.')).toBeLessThan(0) | ||||
|     expect(localConfigContent.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||
|  | ||||
|     // Assert credentials config file deleted | ||||
|     credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||
|       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||
|     ) | ||||
|     expect(credentialsFiles.length).toBe(0) | ||||
|   }) | ||||
|  | ||||
|   const removeGlobalConfig_removesOverride = | ||||
| @@ -701,6 +762,52 @@ 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> { | ||||
| @@ -715,6 +822,7 @@ async function setup(testName: string): Promise<void> { | ||||
|   await fs.promises.mkdir(tempHomedir, {recursive: true}) | ||||
|   process.env['RUNNER_TEMP'] = runnerTemp | ||||
|   process.env['HOME'] = tempHomedir | ||||
|   process.env['GITHUB_WORKSPACE'] = workspace | ||||
|  | ||||
|   // Create git config | ||||
|   globalGitConfigPath = path.join(tempHomedir, '.gitconfig') | ||||
| @@ -733,10 +841,20 @@ async function setup(testName: string): Promise<void> { | ||||
|     checkout: jest.fn(), | ||||
|     checkoutDetach: jest.fn(), | ||||
|     config: jest.fn( | ||||
|       async (key: string, value: string, globalConfig?: boolean) => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|           : localGitConfigPath | ||||
|       async ( | ||||
|         key: string, | ||||
|         value: string, | ||||
|         globalConfig?: boolean, | ||||
|         add?: boolean, | ||||
|         configFile?: string | ||||
|       ) => { | ||||
|         const configPath = | ||||
|           configFile || | ||||
|           (globalConfig | ||||
|             ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|             : localGitConfigPath) | ||||
|         // Ensure directory exists | ||||
|         await fs.promises.mkdir(path.dirname(configPath), {recursive: true}) | ||||
|         await fs.promises.appendFile(configPath, `\n${key} ${value}`) | ||||
|       } | ||||
|     ), | ||||
| @@ -756,6 +874,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(), | ||||
| @@ -794,8 +913,57 @@ async function setup(testName: string): Promise<void> { | ||||
|         return true | ||||
|       } | ||||
|     ), | ||||
|     tryConfigUnsetValue: jest.fn( | ||||
|       async ( | ||||
|         key: string, | ||||
|         value: string, | ||||
|         globalConfig?: boolean | ||||
|       ): Promise<boolean> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|           : localGitConfigPath | ||||
|         let content = await fs.promises.readFile(configPath) | ||||
|         let lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|           .filter(x => x) | ||||
|           .filter(x => !(x.startsWith(key) && x.includes(value))) | ||||
|         await fs.promises.writeFile(configPath, lines.join('\n')) | ||||
|         return true | ||||
|       } | ||||
|     ), | ||||
|     tryDisableAutomaticGarbageCollection: jest.fn(), | ||||
|     tryGetFetchUrl: jest.fn(), | ||||
|     tryGetConfigValues: jest.fn( | ||||
|       async (key: string, globalConfig?: boolean): Promise<string[]> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|           : localGitConfigPath | ||||
|         const content = await fs.promises.readFile(configPath) | ||||
|         const lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|           .filter(x => x && x.startsWith(key)) | ||||
|           .map(x => x.substring(key.length).trim()) | ||||
|         return lines | ||||
|       } | ||||
|     ), | ||||
|     tryGetConfigKeys: jest.fn( | ||||
|       async (pattern: string, globalConfig?: boolean): Promise<string[]> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|           : localGitConfigPath | ||||
|         const content = await fs.promises.readFile(configPath) | ||||
|         const lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|           .filter(x => x) | ||||
|         const keys = lines | ||||
|           .filter(x => new RegExp(pattern).test(x.split(' ')[0])) | ||||
|           .map(x => x.split(' ')[0]) | ||||
|         return [...new Set(keys)] // Remove duplicates | ||||
|       } | ||||
|     ), | ||||
|     tryReset: jest.fn(), | ||||
|     version: jest.fn() | ||||
|   } | ||||
| @@ -830,6 +998,7 @@ async function setup(testName: string): Promise<void> { | ||||
|  | ||||
| async function getActualSshKeyPath(): Promise<string> { | ||||
|   let actualTempFiles = (await fs.promises.readdir(runnerTemp)) | ||||
|     .filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file | ||||
|     .sort() | ||||
|     .map(x => path.join(runnerTemp, x)) | ||||
|   if (actualTempFiles.length === 0) { | ||||
| @@ -843,6 +1012,7 @@ async function getActualSshKeyPath(): Promise<string> { | ||||
|  | ||||
| async function getActualSshKnownHostsPath(): Promise<string> { | ||||
|   let actualTempFiles = (await fs.promises.readdir(runnerTemp)) | ||||
|     .filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file | ||||
|     .sort() | ||||
|     .map(x => path.join(runnerTemp, x)) | ||||
|   if (actualTempFiles.length === 0) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 eric sciple
					eric sciple