UNPKG

infly-libs

Version:

工具组件库

278 lines (248 loc) 9.31 kB
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 };