/** * Build the WeChat upload "项目备注" from recent change summaries. * Prefer recent git commit subjects (Chinese commit messages in this repo); * fall back to the latest CHANGELOG bullets when git history is unavailable. */ const fs = require("node:fs"); const path = require("node:path"); const { execFileSync } = require("node:child_process"); const DEFAULT_MAX_LENGTH = 100; function cleanCommitSubject(subject) { return String(subject || "") .trim() .replace( /^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\([^)]*\))?:\s*/i, "", ) .replace(/\s+/g, " ") .trim(); } function truncateDesc(text, maxLength = DEFAULT_MAX_LENGTH) { const value = String(text || "").trim(); if (!value) { return ""; } if ([...value].length <= maxLength) { return value; } const chars = [...value].slice(0, Math.max(0, maxLength - 1)); return `${chars.join("")}…`; } function collectRecentCommitSubjects(repoRoot, limit = 5) { try { const output = execFileSync( "git", [ "log", `-${Math.max(1, limit * 3)}`, "--pretty=%s", "--", "mini/", "CHANGELOG.md", "Features.md", ], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], }, ); const subjects = []; const seen = new Set(); for (const line of output.split("\n")) { const cleaned = cleanCommitSubject(line); if (!cleaned || seen.has(cleaned)) { continue; } // Skip pure docs/chore noise when better subjects exist. if (/^(说明|文档|记录)/.test(cleaned) && subjects.length > 0) { continue; } seen.add(cleaned); subjects.push(cleaned); if (subjects.length >= limit) { break; } } return subjects; } catch { return []; } } function collectRecentChangelogBullets(repoRoot, limit = 5) { const changelogPath = path.join(repoRoot, "CHANGELOG.md"); if (!fs.existsSync(changelogPath)) { return []; } const lines = fs.readFileSync(changelogPath, "utf8").split(/\r?\n/); const bullets = []; const seen = new Set(); for (const line of lines) { const match = line.match(/^\s*-\s+(.+)\s*$/); if (!match) { continue; } const bullet = match[1].replace(/\s+/g, " ").trim(); if (!bullet || seen.has(bullet)) { continue; } seen.add(bullet); bullets.push(bullet); if (bullets.length >= limit) { break; } } return bullets; } function resolveUploadDesc({ repoRoot, versionLabel = "", maxLength = DEFAULT_MAX_LENGTH, maxItems = 3, } = {}) { const root = repoRoot || process.cwd(); const fromGit = collectRecentCommitSubjects(root, maxItems); const fromChangelog = collectRecentChangelogBullets(root, maxItems); const parts = fromGit.length > 0 ? fromGit : fromChangelog; if (parts.length === 0) { const shortSha = String(versionLabel || "") .trim() .slice(0, 7); return truncateDesc( shortSha ? `CI upload ${shortSha}` : "CI upload", maxLength, ); } // Fit as many summaries as possible within the WeChat remark limit. const selected = []; for (const part of parts) { const candidate = [...selected, part].join(";"); if ([...candidate].length > maxLength) { if (selected.length === 0) { return truncateDesc(part, maxLength); } break; } selected.push(part); } return truncateDesc(selected.join(";"), maxLength); } module.exports = { cleanCommitSubject, collectRecentChangelogBullets, collectRecentCommitSubjects, resolveUploadDesc, truncateDesc, };