mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 01:23:53 +00:00
Handle PAT tokens and credentials in git remote URL parsing (#1225)
This commit is contained in:
@@ -76,6 +76,105 @@ describe('getGitHubRepoInfo', async () => {
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
// Tests for credential formats
|
||||
|
||||
it('returns the owner and repo for URL with classic PAT token (ghp_)', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@github.com/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('returns the owner and repo for URL with fine-grained PAT token (github_pat_)', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://github_pat_xxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@github.com/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('returns the owner and repo for URL with username:password format', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://username:password@github.com/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('returns the owner and repo for URL with OAuth token (oauth2:token)', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://oauth2:gho_xxxxxxxxxxxx@github.com/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('returns the owner and repo for URL with GitHub Actions token (x-access-token)', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://x-access-token:ghs_xxxxxxxxxxxx@github.com/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
// Tests for case insensitivity
|
||||
|
||||
it('returns the owner and repo for URL with uppercase GITHUB.COM', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://GITHUB.COM/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('returns the owner and repo for URL with mixed case GitHub.Com', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://GitHub.Com/owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
// Tests for SSH format
|
||||
|
||||
it('returns the owner and repo for SSH URL', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'git@github.com:owner/repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('throws for non-GitHub SSH URL', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'git@gitlab.com:owner/repo.git',
|
||||
);
|
||||
expect(() => {
|
||||
getGitHubRepoInfo();
|
||||
}).toThrowError(/Owner & repo could not be extracted from remote URL/);
|
||||
});
|
||||
|
||||
// Tests for edge cases
|
||||
|
||||
it('returns the owner and repo for URL without .git suffix', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://github.com/owner/repo',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' });
|
||||
});
|
||||
|
||||
it('throws for non-GitHub HTTPS URL', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://gitlab.com/owner/repo.git',
|
||||
);
|
||||
expect(() => {
|
||||
getGitHubRepoInfo();
|
||||
}).toThrowError(/Owner & repo could not be extracted from remote URL/);
|
||||
});
|
||||
|
||||
it('handles repo names containing .git substring', async () => {
|
||||
vi.mocked(child_process.execSync).mockReturnValueOnce(
|
||||
'https://github.com/owner/my.git.repo.git',
|
||||
);
|
||||
expect(getGitHubRepoInfo()).toEqual({
|
||||
owner: 'owner',
|
||||
repo: 'my.git.repo',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGitRepoRoot', async () => {
|
||||
|
||||
@@ -103,17 +103,38 @@ export function getGitHubRepoInfo(): { owner: string; repo: string } {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
|
||||
// Matches either https://github.com/owner/repo.git or git@github.com:owner/repo.git
|
||||
const match = remoteUrl.match(
|
||||
/(?:https?:\/\/|git@)github\.com(?::|\/)([^/]+)\/([^/]+?)(?:\.git)?$/,
|
||||
);
|
||||
|
||||
// If the regex fails match, throw an error.
|
||||
if (!match || !match[1] || !match[2]) {
|
||||
// Handle SCP-style SSH URLs (git@github.com:owner/repo.git)
|
||||
let urlToParse = remoteUrl;
|
||||
if (remoteUrl.startsWith('git@github.com:')) {
|
||||
urlToParse = remoteUrl.replace('git@github.com:', '');
|
||||
} else if (remoteUrl.startsWith('git@')) {
|
||||
// SSH URL for a different provider (GitLab, Bitbucket, etc.)
|
||||
throw new Error(
|
||||
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { owner: match[1], repo: match[2] };
|
||||
let parsedUrl: URL;
|
||||
try {
|
||||
parsedUrl = new URL(urlToParse, 'https://github.com');
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (parsedUrl.host !== 'github.com') {
|
||||
throw new Error(
|
||||
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
const parts = parsedUrl.pathname.split('/').filter((part) => part !== '');
|
||||
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
||||
throw new Error(
|
||||
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { owner: parts[0], repo: parts[1].replace(/\.git$/, '') };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user