This commit is contained in:
Drew Ballance 2026-04-28 13:01:19 -05:00 committed by GitHub
commit 2afd9d1372
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 111 additions and 6 deletions

View File

@ -22,6 +22,11 @@ let settings: IGitSourceSettings
let sshPath: string
let githubServerUrl: string
// Helper function to normalize path separators to forward slashes
function convertBackslashes(file: string): string {
return file.replace(/\\/g, '/')
}
describe('git-auth-helper tests', () => {
beforeAll(async () => {
// SSH
@ -238,6 +243,87 @@ describe('git-auth-helper tests', () => {
expect(setSecretSpy).toHaveBeenCalledWith(expectedSecret)
})
const configureAuth_resolvesSymlinksInIncludeIfGitdir =
'configureAuth resolves symlinks in includeIf gitdir'
it(configureAuth_resolvesSymlinksInIncludeIfGitdir, async () => {
if (isWindows) {
process.stdout.write(
`Skipped test "${configureAuth_resolvesSymlinksInIncludeIfGitdir}". Symlink creation requires admin privileges on Windows.\n`
)
return
}
// Arrange
await setup(configureAuth_resolvesSymlinksInIncludeIfGitdir)
const symlinkPath = path.join(path.dirname(workspace), 'workspace-symlink')
try {
// Ensure no pre-existing symlink or file remains at this path
await fs.promises.rm(symlinkPath, {force: true})
// Create a symlink pointing to the real workspace directory
await fs.promises.symlink(workspace, symlinkPath)
// Make git appear to be operating from the symlink path
const mockGetWorkingDirectory = git.getWorkingDirectory as jest.Mock
mockGetWorkingDirectory.mockReturnValue(symlinkPath)
process.env['GITHUB_WORKSPACE'] = symlinkPath
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
// Act
await authHelper.configureAuth()
// Assert the host includeIf uses the real resolved path, not the symlink path
const localConfigContent = (
await fs.promises.readFile(localGitConfigPath)
).toString()
const realGitDir = convertBackslashes(
await fs.promises.realpath(path.join(symlinkPath, '.git'))
)
const symlinkGitDir = convertBackslashes(path.join(symlinkPath, '.git'))
expect(realGitDir).not.toBe(symlinkGitDir) // sanity check: paths differ
expect(
localConfigContent.indexOf(`includeIf.gitdir:${realGitDir}.path`)
).toBeGreaterThanOrEqual(0)
expect(localConfigContent.indexOf(symlinkGitDir)).toBeLessThan(0)
} finally {
// Clean up symlink (or any file) at the symlink path
await fs.promises.rm(symlinkPath, {force: true})
}
})
const configureAuth_fallsBackWhenRealpathSyncFails =
'configureAuth falls back to constructed path when realpathSync fails'
it(configureAuth_fallsBackWhenRealpathSyncFails, async () => {
// Arrange
await setup(configureAuth_fallsBackWhenRealpathSyncFails)
// Use a nonexistent path so realpathSync throws ENOENT naturally,
// exercising the catch fallback in configureToken()
const nonexistentPath = path.join(runnerTemp, 'does-not-exist')
const mockGetWorkingDirectory = git.getWorkingDirectory as jest.Mock
mockGetWorkingDirectory.mockReturnValue(nonexistentPath)
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
// Act - should not throw despite realpathSync failure
await authHelper.configureAuth()
// Assert the fallback constructed path is used in the includeIf entry
const localConfigContent = (
await fs.promises.readFile(localGitConfigPath)
).toString()
const fallbackGitDir = convertBackslashes(
path.join(nonexistentPath, '.git')
)
expect(
localConfigContent.indexOf(`includeIf.gitdir:${fallbackGitDir}.path`)
).toBeGreaterThanOrEqual(0)
})
const setsSshCommandEnvVarWhenPersistCredentialsFalse =
'sets SSH command env var when persist-credentials false'
it(setsSshCommandEnvVarWhenPersistCredentialsFalse, async () => {

16
dist/index.js vendored
View File

@ -406,9 +406,19 @@ class GitAuthHelper {
);
}
else {
// Host git directory
let gitDir = path.join(this.git.getWorkingDirectory(), '.git');
gitDir = gitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
// Host git directory - resolve symlinks so includeIf gitdir matching works
// on self-hosted runners where _work is a symlink to an external volume.
let gitDir;
try {
const constructed = path.join(this.git.getWorkingDirectory(), '.git');
const resolved = yield fs.promises.realpath(constructed);
gitDir = resolved.replace(/\\/g, '/');
}
catch (_a) {
// Fall back to constructed path if realpath fails
gitDir = path.join(this.git.getWorkingDirectory(), '.git');
gitDir = gitDir.replace(/\\/g, '/');
}
// Configure host includeIf
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`;
yield this.git.config(hostIncludeKey, credentialsConfigPath);

View File

@ -366,9 +366,18 @@ class GitAuthHelper {
true // globalConfig?
)
} else {
// Host git directory
let gitDir = path.join(this.git.getWorkingDirectory(), '.git')
gitDir = gitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows
// Host git directory - resolve symlinks so includeIf gitdir matching works
// on self-hosted runners where _work is a symlink to an external volume.
let gitDir: string
try {
const constructed = path.join(this.git.getWorkingDirectory(), '.git')
const resolved = await fs.promises.realpath(constructed)
gitDir = resolved.replace(/\\/g, '/')
} catch {
// Fall back to constructed path if realpath fails
gitDir = path.join(this.git.getWorkingDirectory(), '.git')
gitDir = gitDir.replace(/\\/g, '/')
}
// Configure host includeIf
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`