v6
This commit is contained in:
1 parent
b1d7621cc7
commit
feafc79e51
27 files changed
+1713
-626
No files matched your search
+323
-261
@@ -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.*[email protected]:/
|
||||
)
|
||||
expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch(
|
||||
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(
|
||||
/url.*insteadOf.*[email protected]:/
|
||||
)
|
||||
}
|
||||
@@ -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,112 +705,81 @@ describe('git-auth-helper tests', () => {
|
||||
await setup(removeAuth_removesToken)
|
||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||
await authHelper.configureAuth()
|
||||
let gitConfigContent = (
|
||||
|
||||
// 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)
|
||||
|
||||
// Act
|
||||
await authHelper.removeAuth()
|
||||
// Verify both host and container includeIf entries are present
|
||||
const hostGitDir = path.join(workspace, '.git').replace(/\\/g, '/')
|
||||
expect(
|
||||
localConfigContent.indexOf(`includeIf.gitdir:${hostGitDir}.path`)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
localConfigContent.indexOf('includeIf.gitdir:/github/workspace/.git.path')
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
|
||||
// Assert git config
|
||||
gitConfigContent = (
|
||||
await fs.promises.readFile(localGitConfigPath)
|
||||
// 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)
|
||||
const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0])
|
||||
|
||||
// Verify credentials file contains the auth token
|
||||
let credentialsContent = (
|
||||
await fs.promises.readFile(credentialsFilePath)
|
||||
).toString()
|
||||
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
||||
})
|
||||
|
||||
const removeAuth_removesV6StyleCredentials =
|
||||
'removeAuth removes v6 style credentials'
|
||||
it(removeAuth_removesV6StyleCredentials, async () => {
|
||||
// Arrange
|
||||
await setup(removeAuth_removesV6StyleCredentials)
|
||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||
await authHelper.configureAuth()
|
||||
|
||||
// Manually create v6-style credentials that would be left by v6
|
||||
const credentialsFileName =
|
||||
'git-credentials-12345678-1234-1234-1234-123456789abc.config'
|
||||
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
||||
const basicCredential = Buffer.from(
|
||||
`x-access-token:${settings.authToken}`,
|
||||
'utf8'
|
||||
).toString('base64')
|
||||
const credentialsContent = `[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic ${basicCredential}\n`
|
||||
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
||||
|
||||
// Add includeIf entries to local git config (simulating v6 configuration)
|
||||
const hostGitDir = path.join(workspace, '.git').replace(/\\/g, '/')
|
||||
await fs.promises.appendFile(
|
||||
localGitConfigPath,
|
||||
`[includeIf "gitdir:${hostGitDir}/"]\n\tpath = ${credentialsFilePath}\n`
|
||||
)
|
||||
await fs.promises.appendFile(
|
||||
localGitConfigPath,
|
||||
`[includeIf "gitdir:/github/workspace/.git/"]\n\tpath = /github/runner_temp/${credentialsFileName}\n`
|
||||
)
|
||||
|
||||
// Verify v6 style config exists
|
||||
let gitConfigContent = (
|
||||
await fs.promises.readFile(localGitConfigPath)
|
||||
).toString()
|
||||
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
gitConfigContent.indexOf(credentialsFilePath)
|
||||
credentialsContent.indexOf(
|
||||
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
||||
)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
await fs.promises.stat(credentialsFilePath) // Verify file exists
|
||||
|
||||
// Mock the git methods to handle v6 cleanup
|
||||
const mockTryGetConfigKeys = git.tryGetConfigKeys as jest.Mock<any, any>
|
||||
mockTryGetConfigKeys.mockResolvedValue([
|
||||
`includeIf.gitdir:${hostGitDir}/.path`,
|
||||
'includeIf.gitdir:/github/workspace/.git/.path'
|
||||
])
|
||||
|
||||
const mockTryGetConfigValues = git.tryGetConfigValues as jest.Mock<any, any>
|
||||
mockTryGetConfigValues.mockImplementation(async (key: string) => {
|
||||
if (key === `includeIf.gitdir:${hostGitDir}/.path`) {
|
||||
return [credentialsFilePath]
|
||||
}
|
||||
if (key === 'includeIf.gitdir:/github/workspace/.git/.path') {
|
||||
return [`/github/runner_temp/${credentialsFileName}`]
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const mockTryConfigUnsetValue = git.tryConfigUnsetValue as jest.Mock<
|
||||
any,
|
||||
any
|
||||
>
|
||||
mockTryConfigUnsetValue.mockImplementation(
|
||||
async (
|
||||
key: string,
|
||||
value: string,
|
||||
globalConfig?: boolean,
|
||||
configPath?: string
|
||||
) => {
|
||||
const targetPath = configPath || localGitConfigPath
|
||||
let content = await fs.promises.readFile(targetPath, 'utf8')
|
||||
// Remove the includeIf section
|
||||
const lines = content
|
||||
.split('\n')
|
||||
.filter(line => !line.includes('includeIf') && !line.includes(value))
|
||||
await fs.promises.writeFile(targetPath, lines.join('\n'))
|
||||
return true
|
||||
}
|
||||
// Verify the includeIf entries point to the credentials file
|
||||
const containerCredentialsPath = path.posix.join(
|
||||
'/github/runner_temp',
|
||||
path.basename(credentialsFilePath)
|
||||
)
|
||||
expect(
|
||||
localConfigContent.indexOf(credentialsFilePath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
localConfigContent.indexOf(containerCredentialsPath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
|
||||
// Act
|
||||
await authHelper.removeAuth()
|
||||
|
||||
// Assert includeIf entries removed from local git config
|
||||
gitConfigContent = (
|
||||
// Assert all includeIf entries removed from local git config
|
||||
localConfigContent = (
|
||||
await fs.promises.readFile(localGitConfigPath)
|
||||
).toString()
|
||||
expect(gitConfigContent.indexOf('includeIf')).toBeLessThan(0)
|
||||
expect(gitConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||
expect(localConfigContent.indexOf('includeIf.gitdir:')).toBeLessThan(0)
|
||||
expect(
|
||||
localConfigContent.indexOf(`includeIf.gitdir:${hostGitDir}.path`)
|
||||
).toBeLessThan(0)
|
||||
expect(
|
||||
localConfigContent.indexOf('includeIf.gitdir:/github/workspace/.git.path')
|
||||
).toBeLessThan(0)
|
||||
expect(localConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||
expect(localConfigContent.indexOf(containerCredentialsPath)).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)
|
||||
|
||||
// Verify credentials file no longer exists on disk
|
||||
try {
|
||||
await fs.promises.stat(credentialsFilePath)
|
||||
throw new Error('Credentials file should have been deleted')
|
||||
@@ -776,113 +790,108 @@ describe('git-auth-helper tests', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const removeAuth_removesV6StyleCredentialsFromSubmodules =
|
||||
'removeAuth removes v6 style credentials from submodules'
|
||||
it(removeAuth_removesV6StyleCredentialsFromSubmodules, async () => {
|
||||
const removeAuth_removesTokenFromSubmodules =
|
||||
'removeAuth removes token from submodules'
|
||||
it(removeAuth_removesTokenFromSubmodules, async () => {
|
||||
// Arrange
|
||||
await setup(removeAuth_removesV6StyleCredentialsFromSubmodules)
|
||||
await setup(removeAuth_removesTokenFromSubmodules)
|
||||
|
||||
// Create fake submodule config paths
|
||||
const submodule1Dir = path.join(workspace, '.git', 'modules', 'submodule-1')
|
||||
const submodule2Dir = path.join(workspace, '.git', 'modules', 'submodule-2')
|
||||
const submodule1ConfigPath = path.join(submodule1Dir, 'config')
|
||||
const submodule2ConfigPath = path.join(submodule2Dir, 'config')
|
||||
|
||||
await fs.promises.mkdir(submodule1Dir, {recursive: true})
|
||||
await fs.promises.mkdir(submodule2Dir, {recursive: true})
|
||||
await fs.promises.writeFile(submodule1ConfigPath, '')
|
||||
await fs.promises.writeFile(submodule2ConfigPath, '')
|
||||
|
||||
// Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove)
|
||||
const mockGetSubmoduleConfigPaths =
|
||||
git.getSubmoduleConfigPaths as jest.Mock<any, any>
|
||||
mockGetSubmoduleConfigPaths.mockResolvedValue([
|
||||
submodule1ConfigPath,
|
||||
submodule2ConfigPath
|
||||
])
|
||||
|
||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||
await authHelper.configureAuth()
|
||||
await authHelper.configureSubmoduleAuth()
|
||||
|
||||
// Create v6-style credentials file
|
||||
const credentialsFileName =
|
||||
'git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config'
|
||||
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
||||
const basicCredential = Buffer.from(
|
||||
`x-access-token:${settings.authToken}`,
|
||||
'utf8'
|
||||
).toString('base64')
|
||||
const credentialsContent = `[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic ${basicCredential}\n`
|
||||
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
||||
|
||||
// Add includeIf entries to submodule config
|
||||
const submodule1GitDir = submodule1Dir.replace(/\\/g, '/')
|
||||
await fs.promises.appendFile(
|
||||
submodule1ConfigPath,
|
||||
`[includeIf "gitdir:${submodule1GitDir}/"]\n\tpath = ${credentialsFilePath}\n`
|
||||
// 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)
|
||||
const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0])
|
||||
|
||||
// Verify submodule config has includeIf entry
|
||||
let submoduleConfigContent = (
|
||||
// Verify submodule 1 config has includeIf entries
|
||||
let submodule1Content = (
|
||||
await fs.promises.readFile(submodule1ConfigPath)
|
||||
).toString()
|
||||
expect(submoduleConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(
|
||||
0
|
||||
)
|
||||
const submodule1GitDir = submodule1Dir.replace(/\\/g, '/')
|
||||
expect(
|
||||
submoduleConfigContent.indexOf(credentialsFilePath)
|
||||
submodule1Content.indexOf(`includeIf.gitdir:${submodule1GitDir}.path`)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
submodule1Content.indexOf(credentialsFilePath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
|
||||
// Mock getSubmoduleConfigPaths
|
||||
const mockGetSubmoduleConfigPaths =
|
||||
git.getSubmoduleConfigPaths as jest.Mock<any, any>
|
||||
mockGetSubmoduleConfigPaths.mockResolvedValue([submodule1ConfigPath])
|
||||
// Verify submodule 2 config has includeIf entries
|
||||
let submodule2Content = (
|
||||
await fs.promises.readFile(submodule2ConfigPath)
|
||||
).toString()
|
||||
const submodule2GitDir = submodule2Dir.replace(/\\/g, '/')
|
||||
expect(
|
||||
submodule2Content.indexOf(`includeIf.gitdir:${submodule2GitDir}.path`)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
submodule2Content.indexOf(credentialsFilePath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
|
||||
// Mock tryGetConfigKeys for submodule
|
||||
const mockTryGetConfigKeys = git.tryGetConfigKeys as jest.Mock<any, any>
|
||||
mockTryGetConfigKeys.mockImplementation(
|
||||
async (pattern: string, globalConfig?: boolean, configPath?: string) => {
|
||||
if (configPath === submodule1ConfigPath) {
|
||||
return [`includeIf.gitdir:${submodule1GitDir}/.path`]
|
||||
}
|
||||
return []
|
||||
}
|
||||
// Verify both host and container paths are in each submodule config
|
||||
const containerCredentialsPath = path.posix.join(
|
||||
'/github/runner_temp',
|
||||
path.basename(credentialsFilePath)
|
||||
)
|
||||
expect(
|
||||
submodule1Content.indexOf(containerCredentialsPath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
submodule2Content.indexOf(containerCredentialsPath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
|
||||
// Mock tryGetConfigValues for submodule
|
||||
const mockTryGetConfigValues = git.tryGetConfigValues as jest.Mock<any, any>
|
||||
mockTryGetConfigValues.mockImplementation(
|
||||
async (key: string, globalConfig?: boolean, configPath?: string) => {
|
||||
if (
|
||||
configPath === submodule1ConfigPath &&
|
||||
key === `includeIf.gitdir:${submodule1GitDir}/.path`
|
||||
) {
|
||||
return [credentialsFilePath]
|
||||
}
|
||||
return []
|
||||
}
|
||||
)
|
||||
|
||||
// Mock tryConfigUnsetValue for submodule
|
||||
const mockTryConfigUnsetValue = git.tryConfigUnsetValue as jest.Mock<
|
||||
any,
|
||||
any
|
||||
>
|
||||
mockTryConfigUnsetValue.mockImplementation(
|
||||
async (
|
||||
key: string,
|
||||
value: string,
|
||||
globalConfig?: boolean,
|
||||
configPath?: string
|
||||
) => {
|
||||
const targetPath = configPath || localGitConfigPath
|
||||
let content = await fs.promises.readFile(targetPath, 'utf8')
|
||||
const lines = content
|
||||
.split('\n')
|
||||
.filter(line => !line.includes('includeIf') && !line.includes(value))
|
||||
await fs.promises.writeFile(targetPath, lines.join('\n'))
|
||||
return true
|
||||
}
|
||||
)
|
||||
|
||||
// Act
|
||||
// Act - ensure mock persists for removeAuth
|
||||
mockGetSubmoduleConfigPaths.mockResolvedValue([
|
||||
submodule1ConfigPath,
|
||||
submodule2ConfigPath
|
||||
])
|
||||
await authHelper.removeAuth()
|
||||
|
||||
// Assert submodule includeIf entries removed
|
||||
submoduleConfigContent = (
|
||||
// Assert submodule 1 includeIf entries removed
|
||||
submodule1Content = (
|
||||
await fs.promises.readFile(submodule1ConfigPath)
|
||||
).toString()
|
||||
expect(submoduleConfigContent.indexOf('includeIf')).toBeLessThan(0)
|
||||
expect(submoduleConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||
expect(submodule1Content.indexOf('includeIf.gitdir:')).toBeLessThan(0)
|
||||
expect(submodule1Content.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||
expect(submodule1Content.indexOf(containerCredentialsPath)).toBeLessThan(0)
|
||||
|
||||
// Assert credentials file deleted
|
||||
// Assert submodule 2 includeIf entries removed
|
||||
submodule2Content = (
|
||||
await fs.promises.readFile(submodule2ConfigPath)
|
||||
).toString()
|
||||
expect(submodule2Content.indexOf('includeIf.gitdir:')).toBeLessThan(0)
|
||||
expect(submodule2Content.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||
expect(submodule2Content.indexOf(containerCredentialsPath)).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)
|
||||
|
||||
// Verify credentials file no longer exists on disk
|
||||
try {
|
||||
await fs.promises.stat(credentialsFilePath)
|
||||
throw new Error('Credentials file should have been deleted')
|
||||
@@ -893,65 +902,6 @@ describe('git-auth-helper tests', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const removeAuth_skipsV6CleanupWhenEnvVarSet =
|
||||
'removeAuth skips v6 cleanup when ACTIONS_CHECKOUT_SKIP_V6_CLEANUP is set'
|
||||
it(removeAuth_skipsV6CleanupWhenEnvVarSet, async () => {
|
||||
// Arrange
|
||||
await setup(removeAuth_skipsV6CleanupWhenEnvVarSet)
|
||||
|
||||
// Set the skip environment variable
|
||||
process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'] = '1'
|
||||
|
||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||
await authHelper.configureAuth()
|
||||
|
||||
// Create v6-style credentials file in RUNNER_TEMP
|
||||
const credentialsFileName = 'git-credentials-test-uuid-1234-5678.config'
|
||||
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
||||
const credentialsContent =
|
||||
'[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic token\n'
|
||||
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
||||
|
||||
// Add includeIf section to local git config (separate from http.* config)
|
||||
const includeIfSection = `\n[includeIf "gitdir:/some/path/.git/"]\n\tpath = ${credentialsFilePath}\n`
|
||||
await fs.promises.appendFile(localGitConfigPath, includeIfSection)
|
||||
|
||||
// Verify v6 style config exists
|
||||
let gitConfigContent = (
|
||||
await fs.promises.readFile(localGitConfigPath)
|
||||
).toString()
|
||||
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
||||
await fs.promises.stat(credentialsFilePath) // Verify file exists
|
||||
|
||||
// Act
|
||||
await authHelper.removeAuth()
|
||||
|
||||
// Assert v5 cleanup still happened (http.* removed)
|
||||
gitConfigContent = (
|
||||
await fs.promises.readFile(localGitConfigPath)
|
||||
).toString()
|
||||
expect(
|
||||
gitConfigContent.indexOf('http.https://github.com/.extraheader')
|
||||
).toBeLessThan(0)
|
||||
|
||||
// Assert v6 cleanup was skipped - includeIf should still be present
|
||||
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
||||
expect(
|
||||
gitConfigContent.indexOf(credentialsFilePath)
|
||||
).toBeGreaterThanOrEqual(0)
|
||||
|
||||
// Assert credentials file still exists (wasn't deleted)
|
||||
await fs.promises.stat(credentialsFilePath) // File should still exist
|
||||
|
||||
// Assert debug message was logged
|
||||
expect(core.debug).toHaveBeenCalledWith(
|
||||
'Skipping v6 style cleanup due to ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'
|
||||
)
|
||||
|
||||
// Cleanup
|
||||
delete process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP']
|
||||
})
|
||||
|
||||
const removeGlobalConfig_removesOverride =
|
||||
'removeGlobalConfig removes override'
|
||||
it(removeGlobalConfig_removesOverride, async () => {
|
||||
@@ -978,6 +928,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> {
|
||||
@@ -992,6 +988,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')
|
||||
@@ -1010,10 +1007,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}`)
|
||||
}
|
||||
),
|
||||
@@ -1033,6 +1040,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(),
|
||||
@@ -1071,20 +1079,72 @@ async function setup(testName: string): Promise<void> {
|
||||
return true
|
||||
}
|
||||
),
|
||||
tryConfigUnsetValue: jest.fn(
|
||||
async (
|
||||
key: string,
|
||||
value: string,
|
||||
globalConfig?: boolean,
|
||||
configPath?: string
|
||||
): Promise<boolean> => {
|
||||
const targetConfigPath =
|
||||
configPath ||
|
||||
(globalConfig
|
||||
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||
: localGitConfigPath)
|
||||
let content = await fs.promises.readFile(targetConfigPath)
|
||||
let lines = content
|
||||
.toString()
|
||||
.split('\n')
|
||||
.filter(x => x)
|
||||
.filter(x => !(x.startsWith(key) && x.includes(value)))
|
||||
await fs.promises.writeFile(targetConfigPath, lines.join('\n'))
|
||||
return true
|
||||
}
|
||||
),
|
||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||
tryGetFetchUrl: jest.fn(),
|
||||
getSubmoduleConfigPaths: jest.fn(async () => {
|
||||
return []
|
||||
}),
|
||||
tryConfigUnsetValue: jest.fn(async () => {
|
||||
return true
|
||||
}),
|
||||
tryGetConfigValues: jest.fn(async () => {
|
||||
return []
|
||||
}),
|
||||
tryGetConfigKeys: jest.fn(async () => {
|
||||
return []
|
||||
}),
|
||||
tryGetConfigValues: jest.fn(
|
||||
async (
|
||||
key: string,
|
||||
globalConfig?: boolean,
|
||||
configPath?: string
|
||||
): Promise<string[]> => {
|
||||
const targetConfigPath =
|
||||
configPath ||
|
||||
(globalConfig
|
||||
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||
: localGitConfigPath)
|
||||
const content = await fs.promises.readFile(targetConfigPath)
|
||||
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,
|
||||
configPath?: string
|
||||
): Promise<string[]> => {
|
||||
const targetConfigPath =
|
||||
configPath ||
|
||||
(globalConfig
|
||||
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||
: localGitConfigPath)
|
||||
const content = await fs.promises.readFile(targetConfigPath)
|
||||
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()
|
||||
}
|
||||
@@ -1120,6 +1180,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) {
|
||||
@@ -1133,6 +1194,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