UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

181 lines (178 loc) 6.55 kB
import { execCommand } from '../node/node-command.mjs'; import { isInGitRepo } from './git.mjs'; import '@babel/runtime/helpers/typeof'; import '../nodejs/child_process.mjs'; import '../nodejs/fs.mjs'; import '../nodejs/path.mjs'; import '../validate/validate.mjs'; function safe(cmd, root) { try { return execCommand(cmd, root, { line: -1, stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch (_) { return ''; } } /** * 解析 `git config --show-origin --get key` 的输出, * 格式:`file:<配置文件路径>\t<值>` * * 通过配置文件路径判断来源: * - 含 `.git/` 或 `config.worktree` → `'local'` * - 其它(~/.gitconfig、/etc/gitconfig 等) → `'global'` */ function parseConfigWithOrigin(output) { if (!output) return null; // --show-origin 输出格式:file:<path>\t<value> // 不用 s 标志(ES2018+),用 [\s\S] 替代 . 以兼容 ES2015 target var match = output.match(/^file:(.+?)\t([\s\S]+)$/); if (!match) return null; var filePath = match[1]; var value = match[2].trim(); // .git/config 或 .git/worktrees/<name>/config.worktree 都算 local // Windows 路径可能是 .git\\config,用正则同时匹配 / 和 \\ var scope = /[\\/]\.git[\\/]/.test(filePath) ? 'local' : 'global'; return { value: value, scope: scope }; } /** * 将 git remote URL 解析成 `group/subgroup/project` 形式。 * * 支持以下格式: * - `git@git.woa.com:pmd-mobile/pixui/pubgm-official.git` * - `ssh://git@git.woa.com:22/pmd-mobile/pixui/pubgm-official.git` * - `https://git.woa.com/pmd-mobile/pixui/pubgm-official.git` * - `http://git.woa.com/pmd-mobile/pixui/pubgm-official` * * @example * ```ts * parseGitProject('git@git.woa.com:pmd-mobile/pixui/pubgm-official.git'); * // => 'pmd-mobile/pixui/pubgm-official' * ``` */ function parseGitProject(remoteUrl) { if (!remoteUrl) return null; var p = String(remoteUrl).trim(); // 先统一去掉末尾 .git p = p.replace(/\.git$/i, ''); // scp-like: git@host:group/sub/project var scpMatch = p.match(/^[^@\s]+@[^:]+:(.+)$/); if (scpMatch) { return scpMatch[1].replace(/^\/+|\/+$/g, '') || null; } // 普通 URL:去掉协议与 host[:port] p = p.replace(/^[a-zA-Z]+:\/\//, ''); var slashIdx = p.indexOf('/'); if (slashIdx >= 0) { p = p.slice(slashIdx + 1); } p = p.replace(/^\/+|\/+$/g, ''); return p || null; } /** * 一次性收集仓库的完整 git 信息(remote / 分支 / 最近一次提交作者 / 当前登录用户)。 * * 与 `getGitCommitInfo` 的差异: * - 提供 **40 位 commitSha**(`getGitCommitInfo.hash` 是 short id) * - 提供 **authorEmail** * - 提供 **当前 git 登录用户**(`user.name` / `user.email`,**仓库级优先 → 全局 → 环境变量**)并标记来源 `userScope` * - 提供 **project** 字段(自动解析 remoteUrl 为 `group/sub/project`) * * 任意一项失败均不影响其它字段。 * * @param root git 仓库根目录,默认 `process.cwd()` * * @example * ```ts * import { getGitFullInfo } from 't-comm'; * * const info = getGitFullInfo(); * // { * // project: 'pmd-mobile/pixui/pubgm-official', * // remoteUrl: 'git@git.woa.com:pmd-mobile/pixui/pubgm-official.git', * // branch: 'develop', * // commitSha: '33c75cb8e78a7388c357749234d46b731daead09', * // authorName: 'mobilehelper', * // authorEmail: 'mobilehelper@tencent.com', * // userName: 'novlan1', * // userEmail: 'novlan1@tencent.com', * // userScope: 'global', * // } * ``` */ function getGitFullInfo(root) { // 不在 git 仓库时直接返回全空结果,避免多个 git 命令刷屏 stderr/堆栈 if (!isInGitRepo(root)) { var envUserName = null; var envUserEmail = null; envUserName = process.env.GIT_AUTHOR_NAME || process.env.GIT_COMMITTER_NAME || process.env.USER || process.env.USERNAME || null; envUserEmail = process.env.GIT_AUTHOR_EMAIL || process.env.GIT_COMMITTER_EMAIL || null; return { project: null, remoteUrl: null, branch: null, commitSha: null, authorName: null, authorEmail: null, userName: envUserName, userEmail: envUserEmail, userScope: 'none' }; } var remoteUrl = safe('git config --get remote.origin.url', root) || null; var branch = safe('git rev-parse --abbrev-ref HEAD', root) || null; var commitSha = safe('git rev-parse HEAD', root) || null; var authorName = safe('git log -1 --pretty=%an', root) || null; var authorEmail = safe('git log -1 --pretty=%ae', root) || null; // 用 --show-origin 一次性获取值和来源作用域(local > global > system) var nameWithOrigin = safe('git config --show-origin --get user.name', root); var emailWithOrigin = safe('git config --show-origin --get user.email', root); var userName = null; var userEmail = null; var userScope = 'none'; var parsedName = parseConfigWithOrigin(nameWithOrigin); var parsedEmail = parseConfigWithOrigin(emailWithOrigin); if (parsedName || parsedEmail) { // 以 user.name 的来源为准;如果 name 没有则用 email 的来源 var resolvedScope = parsedName ? parsedName.scope : parsedEmail.scope; userName = (parsedName === null || parsedName === void 0 ? void 0 : parsedName.value) || null; userEmail = (parsedEmail === null || parsedEmail === void 0 ? void 0 : parsedEmail.value) || null; userScope = resolvedScope; } // 兜底:--show-origin 获取失败时,用 git config --get(不带 --show-origin)尝试获取 if (!userName) { var fallbackName = safe('git config --get user.name', root); if (fallbackName) { userName = fallbackName; } } if (!userEmail) { var fallbackEmail = safe('git config --get user.email', root); if (fallbackEmail) { userEmail = fallbackEmail; } } // 进一步兜底:尝试从系统环境变量获取(极少数 CI 环境会用到) if (!userName) { userName = process.env.GIT_AUTHOR_NAME || process.env.GIT_COMMITTER_NAME || process.env.USER || process.env.USERNAME || null; } if (!userEmail) { userEmail = process.env.GIT_AUTHOR_EMAIL || process.env.GIT_COMMITTER_EMAIL || null; } return { project: parseGitProject(remoteUrl), remoteUrl: remoteUrl, branch: branch, commitSha: commitSha, authorName: authorName, authorEmail: authorEmail, userName: userName, userEmail: userEmail, userScope: userScope }; } export { getGitFullInfo, parseConfigWithOrigin, parseGitProject };