58 lines
2.8 KiB
TypeScript
58 lines
2.8 KiB
TypeScript
/**
|
||
* 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}`
|
||
}
|