t-comm
Version:
专业、稳定、纯粹的工具库
187 lines (182 loc) • 6.73 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var node_nodeCommand = require('../node/node-command.js');
var git_git = require('./git.js');
require('@babel/runtime/helpers/typeof');
require('../nodejs/child_process.js');
require('../nodejs/fs.js');
require('../nodejs/path.js');
require('../validate/validate.js');
function safe(cmd, root) {
try {
return node_nodeCommand.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 (!git_git.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
};
}
exports.getGitFullInfo = getGitFullInfo;
exports.parseConfigWithOrigin = parseConfigWithOrigin;
exports.parseGitProject = parseGitProject;