infly-libs
Version:
工具组件库
278 lines (248 loc) • 9.31 kB
JavaScript
const path = require("path");
const { execSync } = require("child_process");
/**
* 获取当前 Git 分支名称
* @returns
*/
function getCurrentBranch() {
return execSync("git rev-parse --abbrev-ref HEAD")
.toString()
.trim();
}
/**
* 获取 Git 提交信息
* @returns {Object} 包含提交信息的对象,如果获取失败则返回空对象
*/
function getLatestCommitInfo() {
// 定义 Git 命令
const commands = {
commitMsg: "git log -1 --no-merges --pretty=%B",
branch: "git rev-parse --abbrev-ref HEAD",
fullHash: "git rev-parse HEAD",
author: "git log -1 --no-merges --pretty=%an",
email: "git log -1 --no-merges --pretty=%ae",
date: "git log -1 --no-merges --pretty=%cd --date=iso"
};
// 结果对象
const commitInfo = {};
try {
// 检查当前目录是否是 Git 仓库
execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
// 遍历命令并执行
for (const [key, command] of Object.entries(commands)) {
try {
commitInfo[key] = execSync(`${command}`, { encoding: "utf-8" }).trim();
} catch (cmdError) {
console.warn(global.logColor.warning, `⚠️️ 获取 Git 信息失败(${key}):${cmdError.message}`);
commitInfo[key] = ""; // 失败时设置为空字符串
}
}
} catch (error) {
console.error(
global.logColor.error,
`❌ 获取 Git 信息失败:当前目录不是 Git 仓库或 Git 未安装。错误信息:${error.message}`
);
return {}; // 返回空对象
}
return commitInfo;
}
/**
* 获取指定文件的最后一次提交信息
* @param {String} filePath - 文件路径
* @returns {Object|null} 包含提交信息的对象,如果获取失败则返回 null
*/
function getLastCommitByFile(filePath) {
try {
// 执行 Git 命令获取最后一次提交信息:ml-citation{ref="3,5" data="citationList"}
const cmd = `git log -1 --format="%H|%an|%ae|%at|%s" -- ${filePath}`;
const output = execSync(cmd, { encoding: "utf8" }).trim();
if (!output) return null;
// 解析提交信息:ml-citation{ref="1,5" data="citationList"}
const [hash, author, email, timestamp, message] = output.split("|");
return {
commitMsg: message.trim(),
hash,
author,
email,
date: new Date(parseInt(timestamp) * 1000).toISOString()
};
} catch {
return null;
}
}
/**
* 检查是否构建文件
* @param {String} targetProjectOutputDir 构建文件输出绝对路径
* @param {String} gitStatusLine 带文件状态的
* @returns
*/
function isBuildFile(targetProjectOutputDir = "", gitLine = "", sliceLen = -2) {
// 统一路径分隔符
targetProjectOutputDir = targetProjectOutputDir.replace(/\\/g, "/");
// 取目录的最后 N 级,比如 frontend/ea
const dirFragment = targetProjectOutputDir
.split("/")
.slice(sliceLen)
.join("/");
// 去掉状态前缀 (M, A, D, ?? 等)
const gitPath = gitLine
.trim()
.slice(2)
.trim();
// 判断是否以 dirFragment 开头
return gitPath.startsWith(dirFragment + "/");
}
/**
* 检查 Git 状态
* @param {String} excludeDir - 排除的目录
* @param {String} extraFilter - 额外过滤条件
* @param {Object} extraParams - 额外的配置
*/
function checkGitStatus(excludeDir, extraFilter, extraParams) {
const {
deleteOldFile = [],
versionFileName = "version-config.json",
gitAutoPushRepos,
targetProjectOutputDir,
targetProjectOutputDirCmd = gitAutoPushRepos ? ` -C ${targetProjectOutputDir}` : "",
onlyCheckStatus,
startLogText = "Git自动提交开始执行, 正在检查非构建文件"
} = extraParams || {};
try {
console.log(global.logColor.success, `🚀 ${startLogText}`);
// 获取所有未跟踪和已修改的文件列表
const files = execSync(`git${targetProjectOutputDirCmd} status --porcelain -z`, { encoding: "utf8" })
.toString()
.split("\u0000")
.filter(line => line.trim());
// .map((line) => line.substring(3).trim()); // 提取文件路径
const excludeFiles = []; // 非指定构建相关文件
const includeFiles = []; // 指定的构建相关文件
if (onlyCheckStatus) {
return files || [];
}
files.forEach(file => {
const existsDelFile = deleteOldFile.find(item => file.includes(item));
if (
file.includes(`${excludeDir}/`) ||
file.includes(versionFileName) ||
file.includes(extraFilter) ||
isBuildFile(targetProjectOutputDir, file) ||
file.includes(`${gitAutoPushRepos}/`) || // 放宽文件判断条件,因为可能存在同时构建frontend/ea、frontend/e的情况
existsDelFile
) {
includeFiles.push(file);
} else {
excludeFiles.push(file);
}
});
if (excludeFiles.length > 0) {
throw new Error(`存在非构建文件未提交,请手动确定提交信息,本次自动提交终止\n${excludeFiles.join("\n")}`);
}
} catch (err) {
console.error(global.logColor.error, `❌ 自动化构建Git状态检查存在问题:${err.message}`);
process.exit(1);
}
}
function runGitCommand(command, options = {}) {
try {
const result = execSync(command, {
...options,
encoding: "utf8"
});
if (result) {
return result.trim();
}
} catch (error) {
console.error(global.logColor.error, `❌ 执行 Git 命令失败:${error.message}`);
process.exit(1);
}
}
/**
* git自动推送
* @param {String} buildFoldName 构建文件名称
* @param {String} newFileName 新压缩文件名
* @param {Object} extraParams 额外的参数
* @returns
*/
function autoGitProcess(buildFoldName, newFileName, extraParams = {}) {
const {
newVersion,
gitInfo = {},
gitAutoCommitText = gitInfo.commitMsg,
gitAutoPushRepos,
targetProjectOutputDir,
gitAutoPushReposBranchMap = {},
targetProjectOutputDirCmd = gitAutoPushRepos ? ` -C ${targetProjectOutputDir}` : ""
} = extraParams || {};
const { commitMsg, branch } = gitInfo || {};
const gitRepos = gitAutoPushRepos ? "构建仓库" : "当前项目";
let tempGitAutoCommitText = gitAutoCommitText ? gitAutoCommitText.replace("{version}", newVersion) : "";
if (gitAutoPushRepos) {
tempGitAutoCommitText = commitMsg;
}
const tempBranch = gitAutoPushRepos ? gitAutoPushReposBranchMap[branch] : branch;
const addCmd = `git${targetProjectOutputDirCmd} add -A`;
const commitCmd = `git${targetProjectOutputDirCmd} commit -m "${tempGitAutoCommitText}"`;
const pushCommand = `git${targetProjectOutputDirCmd} push origin ${tempBranch}`;
// 无分支则不执行,自动推送到单独仓库必须得配置分支枚举对应
/* "gitAutoPushReposBranchMap": {
"release-zkhTest": "develop-zkhTest"
}, */
if (!tempBranch) {
console.log(global.logColor.error, `❌ 缺少分支配置`);
return;
}
// 先执行构建仓库的推送,防止git提交信息被覆盖
runGitCommand(addCmd);
runGitCommand(commitCmd);
runGitCommand(pushCommand);
// 后执行当前项目的分支自动推送
if (gitAutoPushRepos) {
const overrideParams = { ...extraParams, gitAutoPushRepos: false };
const fileList = checkGitStatus(buildFoldName, newFileName, { ...overrideParams, onlyCheckStatus: true });
if (fileList.length > 0) {
autoGitProcess(buildFoldName, newFileName, overrideParams); // 执行自动推送当前项目的git信息
}
}
console.log(global.logColor.success, `✅ Git ${gitRepos}自动提交和推送成功!`);
}
/**
* 自动分支处理
* @param {Object} config - 分支配置
*/
function autoBranchProcess(config) {
const { gitAutoPushRepos, gitAutoPushReposBranchMap, currentBranch, targetProjectOutputDir, validateConfig } =
config || {};
const tempBranch = gitAutoPushRepos ? gitAutoPushReposBranchMap[currentBranch] : currentBranch;
const targetProjectOutputDirCmd = gitAutoPushRepos ? ` -C ${targetProjectOutputDir}` : "";
const checkoutCmd = `git${targetProjectOutputDirCmd} checkout ${tempBranch}`;
const pullCmd = `git${targetProjectOutputDirCmd} pull origin ${tempBranch}`;
const fileList = checkGitStatus(undefined, undefined, {
onlyCheckStatus: true,
targetProjectOutputDirCmd,
startLogText: "Git自动化开始执行..."
});
if (validateConfig && typeof validateConfig === "function") {
validateConfig();
}
if (!tempBranch) {
console.error(global.logColor.error, `❌ 缺少项目分支对应构建仓库分支配置,当前分支:${currentBranch},请检查`);
process.exit(1);
}
if (fileList.length > 0) {
console.error(global.logColor.error, `❌ 构建仓库存在未提交文件,请检查`);
process.exit(1);
}
runGitCommand(checkoutCmd);
runGitCommand(pullCmd);
}
module.exports = {
getCurrentBranch,
getLatestCommitInfo,
getLastCommitByFile,
checkGitStatus,
autoGitProcess,
runGitCommand,
autoBranchProcess
};