mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33: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' });
|
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 () => {
|
describe('getGitRepoRoot', async () => {
|
||||||
|
|||||||
@@ -103,17 +103,38 @@ export function getGitHubRepoInfo(): { owner: string; repo: string } {
|
|||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
}).trim();
|
}).trim();
|
||||||
|
|
||||||
// Matches either https://github.com/owner/repo.git or git@github.com:owner/repo.git
|
// Handle SCP-style SSH URLs (git@github.com:owner/repo.git)
|
||||||
const match = remoteUrl.match(
|
let urlToParse = remoteUrl;
|
||||||
/(?:https?:\/\/|git@)github\.com(?::|\/)([^/]+)\/([^/]+?)(?:\.git)?$/,
|
if (remoteUrl.startsWith('git@github.com:')) {
|
||||||
);
|
urlToParse = remoteUrl.replace('git@github.com:', '');
|
||||||
|
} else if (remoteUrl.startsWith('git@')) {
|
||||||
// If the regex fails match, throw an error.
|
// SSH URL for a different provider (GitLab, Bitbucket, etc.)
|
||||||
if (!match || !match[1] || !match[2]) {
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`,
|
`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