(cwd: string | undefined, fn: () => void)
| 40 | * used instead of the default GITHUB_TOKEN. |
| 41 | */ |
| 42 | export function withGitToken(cwd: string | undefined, fn: () => void): void { |
| 43 | const token = process.env.BUMPY_GH_TOKEN || process.env.GH_TOKEN; |
| 44 | const server = process.env.GITHUB_SERVER_URL || 'https://github.com'; |
| 45 | |
| 46 | if (!token) { |
| 47 | fn(); |
| 48 | return; |
| 49 | } |
| 50 | |
| 51 | const extraHeaderKey = `http.${server}/.extraheader`; |
| 52 | |
| 53 | // Save and clear any existing credential config set by actions/checkout: |
| 54 | // 1. Direct http.<server>/.extraheader in local config |
| 55 | // 2. includeIf.gitdir entries pointing to credential config files |
| 56 | const savedHeader = tryRunArgs(['git', 'config', '--local', extraHeaderKey], { cwd }); |
| 57 | |
| 58 | const includeIfRaw = tryRunArgs(['git', 'config', '--local', '--get-regexp', '^includeif\\.gitdir:'], { cwd }); |
| 59 | const savedIncludeIfs: Array<{ key: string; value: string }> = []; |
| 60 | if (includeIfRaw) { |
| 61 | for (const line of includeIfRaw.split('\n').filter(Boolean)) { |
| 62 | const spaceIdx = line.indexOf(' '); |
| 63 | if (spaceIdx > 0) { |
| 64 | savedIncludeIfs.push({ key: line.slice(0, spaceIdx), value: line.slice(spaceIdx + 1) }); |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | // Rewrite the remote URL to embed the token — this is the most reliable |
| 70 | // auth method as it bypasses all credential helpers and extraheader issues |
| 71 | const originalUrl = tryRunArgs(['git', 'remote', 'get-url', 'origin'], { cwd }); |
| 72 | const authedUrl = originalUrl ? originalUrl.replace(/^https:\/\//, `https://x-access-token:${token}@`) : null; |
| 73 | |
| 74 | try { |
| 75 | if (savedHeader) { |
| 76 | runArgs(['git', 'config', '--local', '--unset-all', extraHeaderKey], { cwd }); |
| 77 | } |
| 78 | for (const entry of savedIncludeIfs) { |
| 79 | tryRunArgs(['git', 'config', '--local', '--unset', entry.key], { cwd }); |
| 80 | } |
| 81 | if (authedUrl) { |
| 82 | runArgs(['git', 'remote', 'set-url', 'origin', authedUrl], { cwd }); |
| 83 | } |
| 84 | try { |
| 85 | fn(); |
| 86 | } catch (err) { |
| 87 | // Redact token from error messages to prevent leakage in CI logs |
| 88 | const msg = err instanceof Error ? err.message : String(err); |
| 89 | throw new Error(msg.replaceAll(token, '***')); |
| 90 | } |
| 91 | } finally { |
| 92 | // Restore original remote URL |
| 93 | if (originalUrl) { |
| 94 | runArgs(['git', 'remote', 'set-url', 'origin', originalUrl], { cwd }); |
| 95 | } |
| 96 | // Restore previous credential config |
| 97 | if (savedHeader) { |
| 98 | runArgs(['git', 'config', '--local', extraHeaderKey, savedHeader], { cwd }); |
| 99 | } |
no test coverage detected
searching dependent graphs…