mirror of
https://github.com/Tencent/teamai-cli.git
synced 2026-10-04 04:08:30 +08:00
fix(gitcode): handle ssh:// URLs with port + persist clone creds for fetch
Address PR #376 review: - parseGitCodeRepoInput now strips a leading ssh://[user@]host[:port]/ prefix, so `ssh://git@gitcode.com:2222/owner/repo.git` parses to owner/repo instead of owner=ssh:. Adds unit tests for the port and no-port forms. - shallowClone (import cache) embeds oauth2:<token>@ in the clone URL instead of a one-shot http.extraHeader, so the origin remote persists credentials and the twin shallowFetch()'s plain `git fetch` authenticates on private repos without a credential helper. Mirrors the tgit branch; token is redacted in errors via sanitizeGitUrl. Verified live with credential.helper disabled.
This commit is contained in:
@@ -31,6 +31,19 @@ describe('GitCode repo-url parsing', () => {
|
||||
expect(info.repo).toBe('repo');
|
||||
});
|
||||
|
||||
it('parses ssh:// URL with a non-default port', () => {
|
||||
const info = parseGitCodeRepoInput('ssh://git@gitcode.com:2222/owner/repo.git');
|
||||
expect(info.owner).toBe('owner');
|
||||
expect(info.repo).toBe('repo');
|
||||
expect(info.httpsUrl).toBe('https://gitcode.com/owner/repo.git');
|
||||
});
|
||||
|
||||
it('parses ssh:// URL without a port', () => {
|
||||
const info = parseGitCodeRepoInput('ssh://git@gitcode.com/owner/repo.git');
|
||||
expect(info.owner).toBe('owner');
|
||||
expect(info.repo).toBe('repo');
|
||||
});
|
||||
|
||||
it('ignores trailing web-route segments (Gitee-style /pulls/1)', () => {
|
||||
const info = parseGitCodeRepoInput('https://gitcode.com/owner/repo/pulls/1');
|
||||
expect(info.owner).toBe('owner');
|
||||
|
||||
+8
-5
@@ -218,16 +218,19 @@ export async function shallowClone(
|
||||
log.debug(`shallowClone: 无 GITLAB_TOKEN,尝试匿名 HTTPS 克隆`);
|
||||
}
|
||||
} else if (provider === 'gitcode') {
|
||||
// GitCode: PAT 走 HTTP Basic(用户名固定 oauth2),用 http.extraHeader 注入,
|
||||
// token 不进 URL。已实机验证:GitCode git 端点拒绝 Bearer,仅认 Basic oauth2。
|
||||
// 公有云只有 https,故强制 http→https。
|
||||
// GitCode: 把 PAT 以 oauth2:<token>@ 内嵌进 clone URL(而非仅用一次性
|
||||
// http.extraHeader),让 origin remote 持久化凭据——否则后续 shallowFetch()
|
||||
// 的普通 `git fetch` 在无 credential helper 的环境里会对私有仓报
|
||||
// "could not read Username"。已实机验证 GitCode git 端点仅认 Basic oauth2、
|
||||
// 拒绝 Bearer。与 tgit 分支一致。公有云只有 https,故强制 http→https。
|
||||
const token = getGitCodeToken();
|
||||
cloneUrl = url.replace(/^http:\/\//, 'https://');
|
||||
const httpsUrl = url.replace(/^http:\/\//, 'https://');
|
||||
if (token) {
|
||||
extraAuthHeader = buildAuthHeader(token, 'oauth2');
|
||||
cloneUrl = httpsUrl.replace(/^https:\/\//, `https://oauth2:${token}@`);
|
||||
cloneMethod = 'https-token';
|
||||
log.debug(`shallowClone: 使用 HTTPS+token 克隆 gitcode 仓库`);
|
||||
} else {
|
||||
cloneUrl = httpsUrl;
|
||||
cloneMethod = 'https-anonymous';
|
||||
log.debug(`shallowClone: 无 GITCODE_TOKEN,尝试匿名 HTTPS 克隆`);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,22 @@ export const GITCODE_HOST = 'gitcode.com';
|
||||
* two leading path segments are taken as `owner/repo`; anything after is a web
|
||||
* route and is ignored. Unlike GitLab there is no subgroup nesting and no `/-/`
|
||||
* route separator (GitCode uses GitHub/Gitee-style `/owner/repo/pulls/1`).
|
||||
*
|
||||
* Accepted forms: `owner/repo`, `https://gitcode.com/owner/repo(.git)`,
|
||||
* scp-style `git@gitcode.com:owner/repo(.git)`, and `ssh://` URLs including the
|
||||
* port-qualified form GitCode's UI hands out (`ssh://git@gitcode.com:2222/owner/repo.git`).
|
||||
*/
|
||||
export function parseGitCodeRepoInput(input: string): RepoInfo {
|
||||
const trimmed = input.trim();
|
||||
|
||||
// Strip a leading scheme+host (HTTPS) or scp-style `git@host:` prefix, then
|
||||
// split into path segments. The first two segments are owner/repo; the rest
|
||||
// are web-route noise.
|
||||
// Strip a leading scheme+host prefix, then split into path segments. The first
|
||||
// two segments are owner/repo; the rest are web-route noise.
|
||||
// - `ssh://[user@]host[:port]/…` → drop through the first slash after host
|
||||
// (handled before the scp rule; the `[:port]` never contains a `/`)
|
||||
// - `https?://host/…` → drop scheme+host
|
||||
// - scp-style `git@host:…` → drop `git@host:`
|
||||
const withoutPrefix = trimmed
|
||||
.replace(/^ssh:\/\/[^/]+\//i, '')
|
||||
.replace(/^https?:\/\/[^/]+\//i, '')
|
||||
.replace(/^git@[^:]+:/i, '')
|
||||
.replace(/^\/+/, '');
|
||||
@@ -47,7 +55,8 @@ export function parseGitCodeRepoInput(input: string): RepoInfo {
|
||||
' Supported formats:\n' +
|
||||
' owner/repo\n' +
|
||||
` https://${GITCODE_HOST}/owner/repo.git\n` +
|
||||
` git@${GITCODE_HOST}:owner/repo.git`,
|
||||
` git@${GITCODE_HOST}:owner/repo.git\n` +
|
||||
` ssh://git@${GITCODE_HOST}:2222/owner/repo.git`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user