mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-31 13:58:09 +08:00 
			
		
		
		
	add support for submodules (#173)
This commit is contained in:
		| @@ -8,10 +8,13 @@ import {IGitSourceSettings} from '../lib/git-source-settings' | ||||
|  | ||||
| const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper') | ||||
| const originalRunnerTemp = process.env['RUNNER_TEMP'] | ||||
| const originalHome = process.env['HOME'] | ||||
| let workspace: string | ||||
| let gitConfigPath: string | ||||
| let localGitConfigPath: string | ||||
| let globalGitConfigPath: string | ||||
| let runnerTemp: string | ||||
| let git: IGitCommandManager | ||||
| let tempHomedir: string | ||||
| let git: IGitCommandManager & {env: {[key: string]: string}} | ||||
| let settings: IGitSourceSettings | ||||
|  | ||||
| describe('git-auth-helper tests', () => { | ||||
| @@ -23,11 +26,24 @@ describe('git-auth-helper tests', () => { | ||||
|   beforeEach(() => { | ||||
|     // Mock setSecret | ||||
|     jest.spyOn(core, 'setSecret').mockImplementation((secret: string) => {}) | ||||
|  | ||||
|     // Mock error/warning/info/debug | ||||
|     jest.spyOn(core, 'error').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'warning').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'info').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'debug').mockImplementation(jest.fn()) | ||||
|   }) | ||||
|  | ||||
|   afterEach(() => { | ||||
|     // Unregister mocks | ||||
|     jest.restoreAllMocks() | ||||
|  | ||||
|     // Restore HOME | ||||
|     if (originalHome) { | ||||
|       process.env['HOME'] = originalHome | ||||
|     } else { | ||||
|       delete process.env['HOME'] | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   afterAll(() => { | ||||
| @@ -38,10 +54,11 @@ describe('git-auth-helper tests', () => { | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   const configuresAuthHeader = 'configures auth header' | ||||
|   it(configuresAuthHeader, async () => { | ||||
|   const configureAuth_configuresAuthHeader = | ||||
|     'configureAuth configures auth header' | ||||
|   it(configureAuth_configuresAuthHeader, async () => { | ||||
|     // Arrange | ||||
|     await setup(configuresAuthHeader) | ||||
|     await setup(configureAuth_configuresAuthHeader) | ||||
|     expect(settings.authToken).toBeTruthy() // sanity check | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|  | ||||
| @@ -49,7 +66,9 @@ describe('git-auth-helper tests', () => { | ||||
|     await authHelper.configureAuth() | ||||
|  | ||||
|     // Assert config | ||||
|     const configContent = (await fs.promises.readFile(gitConfigPath)).toString() | ||||
|     const configContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     const basicCredential = Buffer.from( | ||||
|       `x-access-token:${settings.authToken}`, | ||||
|       'utf8' | ||||
| @@ -61,32 +80,39 @@ describe('git-auth-helper tests', () => { | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|   }) | ||||
|  | ||||
|   const configuresAuthHeaderEvenWhenPersistCredentialsFalse = | ||||
|     'configures auth header even when persist credentials false' | ||||
|   it(configuresAuthHeaderEvenWhenPersistCredentialsFalse, async () => { | ||||
|     // Arrange | ||||
|     await setup(configuresAuthHeaderEvenWhenPersistCredentialsFalse) | ||||
|     expect(settings.authToken).toBeTruthy() // sanity check | ||||
|     settings.persistCredentials = false | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|  | ||||
|     // Act | ||||
|     await authHelper.configureAuth() | ||||
|  | ||||
|     // Assert config | ||||
|     const configContent = (await fs.promises.readFile(gitConfigPath)).toString() | ||||
|     expect( | ||||
|       configContent.indexOf( | ||||
|         `http.https://github.com/.extraheader AUTHORIZATION` | ||||
|   const configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse = | ||||
|     'configureAuth configures auth header even when persist credentials false' | ||||
|   it( | ||||
|     configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse, | ||||
|     async () => { | ||||
|       // Arrange | ||||
|       await setup( | ||||
|         configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|   }) | ||||
|       expect(settings.authToken).toBeTruthy() // sanity check | ||||
|       settings.persistCredentials = false | ||||
|       const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|  | ||||
|   const registersBasicCredentialAsSecret = | ||||
|     'registers basic credential as secret' | ||||
|   it(registersBasicCredentialAsSecret, async () => { | ||||
|       // Act | ||||
|       await authHelper.configureAuth() | ||||
|  | ||||
|       // Assert config | ||||
|       const configContent = ( | ||||
|         await fs.promises.readFile(localGitConfigPath) | ||||
|       ).toString() | ||||
|       expect( | ||||
|         configContent.indexOf( | ||||
|           `http.https://github.com/.extraheader AUTHORIZATION` | ||||
|         ) | ||||
|       ).toBeGreaterThanOrEqual(0) | ||||
|     } | ||||
|   ) | ||||
|  | ||||
|   const configureAuth_registersBasicCredentialAsSecret = | ||||
|     'configureAuth registers basic credential as secret' | ||||
|   it(configureAuth_registersBasicCredentialAsSecret, async () => { | ||||
|     // Arrange | ||||
|     await setup(registersBasicCredentialAsSecret) | ||||
|     await setup(configureAuth_registersBasicCredentialAsSecret) | ||||
|     expect(settings.authToken).toBeTruthy() // sanity check | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|  | ||||
| @@ -103,14 +129,139 @@ describe('git-auth-helper tests', () => { | ||||
|     expect(setSecretSpy).toHaveBeenCalledWith(expectedSecret) | ||||
|   }) | ||||
|  | ||||
|   const removesToken = 'removes token' | ||||
|   it(removesToken, async () => { | ||||
|   const configureGlobalAuth_copiesGlobalGitConfig = | ||||
|     'configureGlobalAuth copies global git config' | ||||
|   it(configureGlobalAuth_copiesGlobalGitConfig, async () => { | ||||
|     // Arrange | ||||
|     await setup(removesToken) | ||||
|     await setup(configureGlobalAuth_copiesGlobalGitConfig) | ||||
|     await fs.promises.writeFile(globalGitConfigPath, 'value-from-global-config') | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|  | ||||
|     // Act | ||||
|     await authHelper.configureAuth() | ||||
|     await authHelper.configureGlobalAuth() | ||||
|  | ||||
|     // Assert original global config not altered | ||||
|     let configContent = ( | ||||
|       await fs.promises.readFile(globalGitConfigPath) | ||||
|     ).toString() | ||||
|     expect(configContent).toBe('value-from-global-config') | ||||
|  | ||||
|     // Assert temporary global config | ||||
|     expect(git.env['HOME']).toBeTruthy() | ||||
|     const basicCredential = Buffer.from( | ||||
|       `x-access-token:${settings.authToken}`, | ||||
|       'utf8' | ||||
|     ).toString('base64') | ||||
|     configContent = ( | ||||
|       await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig')) | ||||
|     ).toString() | ||||
|     expect( | ||||
|       configContent.indexOf('value-from-global-config') | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|     expect( | ||||
|       configContent.indexOf( | ||||
|         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|   }) | ||||
|  | ||||
|   const configureGlobalAuth_createsNewGlobalGitConfigWhenGlobalDoesNotExist = | ||||
|     'configureGlobalAuth creates new git config when global does not exist' | ||||
|   it( | ||||
|     configureGlobalAuth_createsNewGlobalGitConfigWhenGlobalDoesNotExist, | ||||
|     async () => { | ||||
|       // Arrange | ||||
|       await setup( | ||||
|         configureGlobalAuth_createsNewGlobalGitConfigWhenGlobalDoesNotExist | ||||
|       ) | ||||
|       await io.rmRF(globalGitConfigPath) | ||||
|       const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|  | ||||
|       // Act | ||||
|       await authHelper.configureAuth() | ||||
|       await authHelper.configureGlobalAuth() | ||||
|  | ||||
|       // Assert original global config not recreated | ||||
|       try { | ||||
|         await fs.promises.stat(globalGitConfigPath) | ||||
|         throw new Error( | ||||
|           `Did not expect file to exist: '${globalGitConfigPath}'` | ||||
|         ) | ||||
|       } catch (err) { | ||||
|         if (err.code !== 'ENOENT') { | ||||
|           throw err | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // Assert temporary global config | ||||
|       expect(git.env['HOME']).toBeTruthy() | ||||
|       const basicCredential = Buffer.from( | ||||
|         `x-access-token:${settings.authToken}`, | ||||
|         'utf8' | ||||
|       ).toString('base64') | ||||
|       const configContent = ( | ||||
|         await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig')) | ||||
|       ).toString() | ||||
|       expect( | ||||
|         configContent.indexOf( | ||||
|           `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|         ) | ||||
|       ).toBeGreaterThanOrEqual(0) | ||||
|     } | ||||
|   ) | ||||
|  | ||||
|   const configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse = | ||||
|     'configureSubmoduleAuth does not configure token when persist credentials false' | ||||
|   it( | ||||
|     configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse, | ||||
|     async () => { | ||||
|       // Arrange | ||||
|       await setup( | ||||
|         configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse | ||||
|       ) | ||||
|       settings.persistCredentials = false | ||||
|       const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|       await authHelper.configureAuth() | ||||
|       ;(git.submoduleForeach as jest.Mock<any, any>).mockClear() // reset calls | ||||
|  | ||||
|       // Act | ||||
|       await authHelper.configureSubmoduleAuth() | ||||
|  | ||||
|       // Assert | ||||
|       expect(git.submoduleForeach).not.toHaveBeenCalled() | ||||
|     } | ||||
|   ) | ||||
|  | ||||
|   const configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrue = | ||||
|     'configureSubmoduleAuth configures token when persist credentials true' | ||||
|   it( | ||||
|     configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrue, | ||||
|     async () => { | ||||
|       // Arrange | ||||
|       await setup( | ||||
|         configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrue | ||||
|       ) | ||||
|       const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|       await authHelper.configureAuth() | ||||
|       ;(git.submoduleForeach as jest.Mock<any, any>).mockClear() // reset calls | ||||
|  | ||||
|       // Act | ||||
|       await authHelper.configureSubmoduleAuth() | ||||
|  | ||||
|       // Assert | ||||
|       expect(git.submoduleForeach).toHaveBeenCalledTimes(1) | ||||
|     } | ||||
|   ) | ||||
|  | ||||
|   const removeAuth_removesToken = 'removeAuth removes token' | ||||
|   it(removeAuth_removesToken, async () => { | ||||
|     // Arrange | ||||
|     await setup(removeAuth_removesToken) | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     await authHelper.configureAuth() | ||||
|     let gitConfigContent = ( | ||||
|       await fs.promises.readFile(gitConfigPath) | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     expect(gitConfigContent.indexOf('http.')).toBeGreaterThanOrEqual(0) // sanity check | ||||
|  | ||||
| @@ -118,9 +269,37 @@ describe('git-auth-helper tests', () => { | ||||
|     await authHelper.removeAuth() | ||||
|  | ||||
|     // Assert git config | ||||
|     gitConfigContent = (await fs.promises.readFile(gitConfigPath)).toString() | ||||
|     gitConfigContent = ( | ||||
|       await fs.promises.readFile(localGitConfigPath) | ||||
|     ).toString() | ||||
|     expect(gitConfigContent.indexOf('http.')).toBeLessThan(0) | ||||
|   }) | ||||
|  | ||||
|   const removeGlobalAuth_removesOverride = 'removeGlobalAuth removes override' | ||||
|   it(removeGlobalAuth_removesOverride, async () => { | ||||
|     // Arrange | ||||
|     await setup(removeGlobalAuth_removesOverride) | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     await authHelper.configureAuth() | ||||
|     await authHelper.configureGlobalAuth() | ||||
|     const homeOverride = git.env['HOME'] // Sanity check | ||||
|     expect(homeOverride).toBeTruthy() | ||||
|     await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig')) | ||||
|  | ||||
|     // Act | ||||
|     await authHelper.removeGlobalAuth() | ||||
|  | ||||
|     // Assert | ||||
|     expect(git.env['HOME']).toBeUndefined() | ||||
|     try { | ||||
|       await fs.promises.stat(homeOverride) | ||||
|       throw new Error(`Should have been deleted '${homeOverride}'`) | ||||
|     } catch (err) { | ||||
|       if (err.code !== 'ENOENT') { | ||||
|         throw err | ||||
|       } | ||||
|     } | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| async function setup(testName: string): Promise<void> { | ||||
| @@ -129,14 +308,19 @@ async function setup(testName: string): Promise<void> { | ||||
|   // Directories | ||||
|   workspace = path.join(testWorkspace, testName, 'workspace') | ||||
|   runnerTemp = path.join(testWorkspace, testName, 'runner-temp') | ||||
|   tempHomedir = path.join(testWorkspace, testName, 'home-dir') | ||||
|   await fs.promises.mkdir(workspace, {recursive: true}) | ||||
|   await fs.promises.mkdir(runnerTemp, {recursive: true}) | ||||
|   await fs.promises.mkdir(tempHomedir, {recursive: true}) | ||||
|   process.env['RUNNER_TEMP'] = runnerTemp | ||||
|   process.env['HOME'] = tempHomedir | ||||
|  | ||||
|   // Create git config | ||||
|   gitConfigPath = path.join(workspace, '.git', 'config') | ||||
|   await fs.promises.mkdir(path.join(workspace, '.git'), {recursive: true}) | ||||
|   await fs.promises.writeFile(path.join(workspace, '.git', 'config'), '') | ||||
|   globalGitConfigPath = path.join(tempHomedir, '.gitconfig') | ||||
|   await fs.promises.writeFile(globalGitConfigPath, '') | ||||
|   localGitConfigPath = path.join(workspace, '.git', 'config') | ||||
|   await fs.promises.mkdir(path.dirname(localGitConfigPath), {recursive: true}) | ||||
|   await fs.promises.writeFile(localGitConfigPath, '') | ||||
|  | ||||
|   git = { | ||||
|     branchDelete: jest.fn(), | ||||
| @@ -144,12 +328,20 @@ async function setup(testName: string): Promise<void> { | ||||
|     branchList: jest.fn(), | ||||
|     checkout: jest.fn(), | ||||
|     checkoutDetach: jest.fn(), | ||||
|     config: jest.fn(async (key: string, value: string) => { | ||||
|       await fs.promises.appendFile(gitConfigPath, `\n${key} ${value}`) | ||||
|     }), | ||||
|     config: jest.fn( | ||||
|       async (key: string, value: string, globalConfig?: boolean) => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|           : localGitConfigPath | ||||
|         await fs.promises.appendFile(configPath, `\n${key} ${value}`) | ||||
|       } | ||||
|     ), | ||||
|     configExists: jest.fn( | ||||
|       async (key: string): Promise<boolean> => { | ||||
|         const content = await fs.promises.readFile(gitConfigPath) | ||||
|       async (key: string, globalConfig?: boolean): Promise<boolean> => { | ||||
|         const configPath = globalConfig | ||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||
|           : localGitConfigPath | ||||
|         const content = await fs.promises.readFile(configPath) | ||||
|         const lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
| @@ -157,6 +349,7 @@ async function setup(testName: string): Promise<void> { | ||||
|         return lines.some(x => x.startsWith(key)) | ||||
|       } | ||||
|     ), | ||||
|     env: {}, | ||||
|     fetch: jest.fn(), | ||||
|     getWorkingDirectory: jest.fn(() => workspace), | ||||
|     init: jest.fn(), | ||||
| @@ -165,18 +358,29 @@ async function setup(testName: string): Promise<void> { | ||||
|     lfsInstall: jest.fn(), | ||||
|     log1: jest.fn(), | ||||
|     remoteAdd: jest.fn(), | ||||
|     setEnvironmentVariable: jest.fn(), | ||||
|     removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]), | ||||
|     setEnvironmentVariable: jest.fn((name: string, value: string) => { | ||||
|       git.env[name] = value | ||||
|     }), | ||||
|     submoduleForeach: jest.fn(async () => { | ||||
|       return '' | ||||
|     }), | ||||
|     submoduleSync: jest.fn(), | ||||
|     submoduleUpdate: jest.fn(), | ||||
|     tagExists: jest.fn(), | ||||
|     tryClean: jest.fn(), | ||||
|     tryConfigUnset: jest.fn( | ||||
|       async (key: string): Promise<boolean> => { | ||||
|         let content = await fs.promises.readFile(gitConfigPath) | ||||
|       async (key: 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)) | ||||
|         await fs.promises.writeFile(gitConfigPath, lines.join('\n')) | ||||
|         await fs.promises.writeFile(configPath, lines.join('\n')) | ||||
|         return true | ||||
|       } | ||||
|     ), | ||||
| @@ -191,6 +395,8 @@ async function setup(testName: string): Promise<void> { | ||||
|     commit: '', | ||||
|     fetchDepth: 1, | ||||
|     lfs: false, | ||||
|     submodules: false, | ||||
|     nestedSubmodules: false, | ||||
|     persistCredentials: true, | ||||
|     ref: 'refs/heads/master', | ||||
|     repositoryName: 'my-repo', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user