96 lines
4.1 KiB
JavaScript
96 lines
4.1 KiB
JavaScript
// 基座影响审计:逐行验证本次换肤的改动是否「只改颜色」
|
||
// 做法:解析 git diff -U0,把每段 hunk 的「删除行」与「新增行」都做颜色归一化
|
||
// (#RRGGBB / rgb() / rgba() → 占位符),再比对两侧是否等价。
|
||
// 等价 = 纯换色;不等价 = 有非颜色改动,单独列出待人工判断。
|
||
// 用法: node _design-test/audit-color-only.js
|
||
const { execSync } = require('child_process');
|
||
|
||
const ZF = 'e:/KF_7/ZhenOS-ZF';
|
||
const raw = execSync('git -c core.quotepath=false diff -U0 --no-color', {
|
||
cwd: ZF, maxBuffer: 1 << 28, encoding: 'utf8'
|
||
});
|
||
|
||
const CO = /#[0-9a-fA-F]{3,8}\b|rgba?\([^)]*\)|\bhsl\([^)]*\)/g;
|
||
const norm = s => s.replace(CO, '@C@').replace(/\s+/g, ' ').trim();
|
||
|
||
const isComment = s => {
|
||
const t = s.trim();
|
||
return t.startsWith('//') || t.startsWith('/*') || t.startsWith('*') ||
|
||
t.startsWith('<!--') || t.startsWith('{#') || t === '' ;
|
||
};
|
||
|
||
let curFile = '';
|
||
const offenders = [];
|
||
let hunks = 0, colorOnly = 0, commentOnly = 0, removed = 0, added = 0;
|
||
|
||
const lines = raw.split('\n');
|
||
let bucket = null;
|
||
const flush = () => {
|
||
if (!bucket) return;
|
||
const { file, oldL, newL } = bucket;
|
||
hunks++;
|
||
removed += oldL.length; added += newL.length;
|
||
const a = oldL.map(norm).sort().join('\n');
|
||
const b = newL.map(norm).sort().join('\n');
|
||
if (a === b) { colorOnly++; return; }
|
||
// 只有注释行不同 → 视为注释改动(不影响行为)
|
||
const o = oldL.filter(l => !isComment(l)), n2 = newL.filter(l => !isComment(l));
|
||
if (o.map(norm).sort().join('\n') === n2.map(norm).sort().join('\n')) { commentOnly++; return; }
|
||
offenders.push({ file, oldL, newL });
|
||
bucket = null;
|
||
};
|
||
|
||
for (const ln of lines) {
|
||
if (ln.startsWith('diff --git ')) { flush(); bucket = null; curFile = ln.split(' b/').pop(); continue; }
|
||
if (ln.startsWith('+++ ') || ln.startsWith('--- ') || ln.startsWith('index ') ||
|
||
ln.startsWith('new file') || ln.startsWith('deleted file') || ln.startsWith('similarity')) continue;
|
||
if (ln.startsWith('@@')) { flush(); bucket = { file: curFile, oldL: [], newL: [] }; continue; }
|
||
if (!bucket) continue;
|
||
if (ln.startsWith('-')) bucket.oldL.push(ln.slice(1));
|
||
else if (ln.startsWith('+')) bucket.newL.push(ln.slice(1));
|
||
}
|
||
flush();
|
||
|
||
console.log('=== 换肤改动审计(git diff -U0 逐段比对)===');
|
||
console.log(' 变更行数 新增 ' + added + ' / 删除 ' + removed);
|
||
console.log(' 代码段(hunk) 共 ' + hunks);
|
||
console.log(' · 纯颜色替换 ' + colorOnly);
|
||
console.log(' · 仅注释差异 ' + commentOnly);
|
||
console.log(' · 需要人工复核 ' + offenders.length);
|
||
|
||
// 本次「整文件重写」的样式文件 —— 它们的差异里必然含注释/结构变化,属已知改动,单列
|
||
const REWRITTEN = new Set([
|
||
'ruoyi-ui/src/assets/styles/element-variables.scss',
|
||
'ruoyi-ui/src/assets/styles/theme.scss',
|
||
'ruoyi-ui/src/assets/styles/element-ui.scss',
|
||
'ruoyi-ui/src/assets/styles/variables.scss',
|
||
'ruoyi-ui/src/assets/styles/ruoyi.scss',
|
||
'ruoyi-ui/src/assets/styles/sidebar.scss',
|
||
'ruoyi-ui/src/assets/styles/btn.scss',
|
||
'ruoyi-ui/src/assets/styles/mixin.scss',
|
||
'zhenfang-uni/src/uni.scss'
|
||
]);
|
||
|
||
const byFile = {};
|
||
offenders.forEach(o => { (byFile[o.file] = byFile[o.file] || []).push(o); });
|
||
const keys = Object.keys(byFile);
|
||
const rewritten = keys.filter(k => REWRITTEN.has(k));
|
||
const others = keys.filter(k => !REWRITTEN.has(k));
|
||
|
||
console.log('\n ── 重写类文件(已知改动,含注释/结构重排):' + rewritten.length + ' 个');
|
||
rewritten.forEach(k => console.log(' ' + String(byFile[k].length).padStart(3) + ' 段 ' + k));
|
||
|
||
console.log('\n ── 其它文件的疑似非颜色改动:' + others.length + ' 个文件');
|
||
if (!others.length) {
|
||
console.log(' 无 —— 业务页面/组件/后端相关文件全部只改了颜色');
|
||
} else {
|
||
others.forEach(k => {
|
||
console.log('\n ' + k + ' (' + byFile[k].length + ' 段)');
|
||
byFile[k].slice(0, 4).forEach(o => {
|
||
o.oldL.slice(0, 3).forEach(l => console.log(' - ' + l.trim()));
|
||
o.newL.slice(0, 3).forEach(l => console.log(' + ' + l.trim()));
|
||
console.log(' ---');
|
||
});
|
||
});
|
||
}
|