Files
superwork/vscode/src/forge/github/ghAuth.ts
T

58 lines
2.8 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.
/**
* GitHub 的凭据直接借 GitHub CLI(`gh`)的登录:每次要用时现取 `gh auth token`,插件自己不存 GitHub token。
*/
import { execFile } from 'node:child_process'
import { findExecutable } from '../../shared/executable'
import { ForgeApiError } from '../types'
/** 等 `gh auth token` 返回的最长时间;gh 卡在钥匙串等地方时不能让看板一直等下去。 */
const GH_TOKEN_TIMEOUT_MS = 10_000
/** 没取到 gh 登录凭据时给人看的提示。 */
export const GH_LOGIN_HINT = '未登录 GitHub CLI,请在终端运行 `gh auth login` 登录后重试'
/** gh 登录已失效(GitHub 回 401)时给人看的提示。 */
export const GH_RELOGIN_HINT = 'GitHub CLI 的登录已失效,请在终端运行 `gh auth login` 重新登录后重试'
/** `gh auth token` 超时时给人看的提示,和没登录分开说,免得用户白白重新登录。 */
const GH_TIMEOUT_HINT = `\`gh auth token\` 超时(${GH_TOKEN_TIMEOUT_MS / 1000} 秒内没有返回),取不到 GitHub 凭据;请在终端运行 \`gh auth status\` 看看 gh 是否卡住,然后重试`
/** PATH 里找不到 gh 时再试的安装位置:Homebrew(Apple 芯片、Intel)和系统包管理器。 */
const GH_FALLBACKS = ['/opt/homebrew/bin/gh', '/usr/local/bin/gh', '/usr/bin/gh']
/** [readGhToken] 的结果:取到了给 `token`,取不到给可以直接给人看的 `message`。 */
export type GhToken = { ok: true, token: string } | { ok: false, message: string }
/**
* 取 gh 在 github.com 上登录的 token。
*
* gh 没装、没登录、执行失败或输出为空,`message` 是 [GH_LOGIN_HINT];超时被结束时是超时的说明。
*/
export function readGhToken(): Promise<GhToken> {
const gh = findExecutable('gh', GH_FALLBACKS) ?? 'gh'
return new Promise((resolve) => {
execFile(gh, ['auth', 'token', '--hostname', 'github.com'], { timeout: GH_TOKEN_TIMEOUT_MS, encoding: 'utf8' }, (err, stdout) => {
// `killed` 只在超时被 execFile 结束时为真。
if (err?.killed) {
resolve({ ok: false, message: GH_TIMEOUT_HINT })
return
}
const token = err ? '' : String(stdout).trim()
resolve(token ? { ok: true, token } : { ok: false, message: GH_LOGIN_HINT })
})
})
}
/**
* 设置页查 gh 账号失败时显示的说明;GitHub 回 401 说明登录失效,返回 `undefined`,让设置页按未登录显示。
*
* 其它失败(超时、连不上、GitHub 自己出错)都说成连不上,这时登录可能还有效,不能叫用户重新登录。
*/
export function ghLoginCheckError(err: unknown): string | undefined {
if (err instanceof ForgeApiError && err.status === 401)
return undefined
const reason = err instanceof Error ? err.message : String(err)
return `无法连接 GitHub,暂时确认不了登录状态:${reason}`
}