♻️ refactor(vscode): 代码托管仓库抽成统一接口,Gitea 实现搬到 provider 后面,翻页改看 Link 头

This commit is contained in:
cruldra committed 2026-10-19 07:56:40 +11:00
1 parent c90d84d7f2
commit 14bee9f505
64 files changed
+1881 -2718

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
/**
* 当前工作区对应哪个代码托管仓库:读 git 的 origin 地址,按 host 认出是哪一家,带上凭据造出 [ForgeProvider]。
*
* 所有要访问仓库的地方都从这里拿 [ForgeContext],不再各自去读 remote 和 token。
*/
import type { ExtensionContext } from 'vscode'
import type { Remote } from '../git/remote'
import type { ForgeKind, ForgeProvider } from './types'
import { workspace } from 'vscode'
import { getToken } from '../auth/secrets'
import { detectRepo } from '../git/remote'
import { GiteaProvider } from './gitea/provider'
/** 一个工作区对应的代码托管仓库,和访问它用的 [ForgeProvider]。 */
export interface ForgeContext {
workspaceRoot: string
kind: ForgeKind
host: string
owner: string
repo: string
provider: ForgeProvider
}
/**
* 解析结果;失败时 `message` 是可以直接给人看的原因,`reason` 给需要分情况处理的调用方:
* `no-workspace` 没打开工作区,`no-remote` 没有能认出的 origin,`no-credentials` 没有凭据,`unsupported` 这一家还不支持。
*/
export type ForgeResolution
= | { ok: true, forge: ForgeContext }
| { ok: false, reason: 'no-workspace' | 'no-remote', message: string }
| { ok: false, reason: 'no-credentials' | 'unsupported', message: string, kind: ForgeKind, host: string }
/** host 小写后是 `github.com` 的是 GitHub,其余都当 Gitea;不认 GitHub Enterprise。 */
export function forgeKindForHost(host: string): ForgeKind {
return host.toLowerCase() === 'github.com' ? 'github' : 'gitea'
}
/** 给指定的仓库造 [ForgeContext];webhook 记下的仓库不一定是当前工作区的 origin,所以单独开放。 */
export async function openForge(ctx: ExtensionContext, workspaceRoot: string, remote: Remote): Promise<ForgeResolution> {
const kind = forgeKindForHost(remote.host)
if (kind === 'github')
return { ok: false, reason: 'unsupported', kind, host: remote.host, message: '暂未支持 GitHub 仓库' }
const token = await getToken(ctx, remote.host)
if (!token)
return { ok: false, reason: 'no-credentials', kind, host: remote.host, message: `请先配置 ${remote.host} 的访问令牌` }
return {
ok: true,
forge: {
workspaceRoot,
kind,
host: remote.host,
owner: remote.owner,
repo: remote.repo,
provider: new GiteaProvider({ ...remote, token }),
},
}
}
/** 解析当前工作区(第一个工作区文件夹)的代码托管仓库。 */
export async function resolveForge(ctx: ExtensionContext): Promise<ForgeResolution> {
const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath
if (!workspaceRoot)
return { ok: false, reason: 'no-workspace', message: '请先打开一个工作区文件夹' }
const remote = await detectRepo(workspaceRoot)
if (!remote)
return { ok: false, reason: 'no-remote', message: '当前工作区没有能识别的 origin 远程仓库' }
return openForge(ctx, workspaceRoot, remote)
}
/**
* 同 [resolveForge],解析不出来时抛错。
*
* @throws [Error] 解析失败,`message` 是给人看的原因
*/
export async function requireForge(ctx: ExtensionContext): Promise<ForgeContext> {
const resolved = await resolveForge(ctx)
if (!resolved.ok)
throw new Error(resolved.message)
return resolved.forge
}