Files
superwork/vscode/src/forge/context.ts
T

79 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 当前工作区对应哪个代码托管仓库:读 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'
import { GH_LOGIN_HINT, readGhToken } from './github/ghAuth'
import { GitHubProvider } from './github/provider'
import { forgeKindForHost } from './types'
/** 一个工作区对应的代码托管仓库,和访问它用的 [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` 没有凭据。
*/
export type ForgeResolution
= | { ok: true, forge: ForgeContext }
| { ok: false, reason: 'no-workspace' | 'no-remote', message: string }
| { ok: false, reason: 'no-credentials', message: string, kind: ForgeKind, host: string }
/**
* 给指定的仓库造 [ForgeContext];webhook 记下的仓库不一定是当前工作区的 origin,所以单独开放。
*
* GitHub 的凭据现取 gh 的登录,Gitea 的凭据是用户在设置里填、存在 SecretStorage 里的 token。
*/
export async function openForge(ctx: ExtensionContext, workspaceRoot: string, remote: Remote): Promise<ForgeResolution> {
const kind = forgeKindForHost(remote.host)
const repo = { workspaceRoot, kind, host: remote.host, owner: remote.owner, repo: remote.repo }
if (kind === 'github') {
const token = await readGhToken()
if (!token)
return { ok: false, reason: 'no-credentials', kind, host: remote.host, message: GH_LOGIN_HINT }
return { ok: true, forge: { ...repo, provider: new GitHubProvider({ owner: remote.owner, repo: remote.repo, token }) } }
}
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: { ...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
}