108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import { existsSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
import type { EnvPolicyInput, RiskScanInput } from '../types.js';
|
|
import { tryExecFile } from '../core/exec.js';
|
|
import { detectRepoType } from '../core/project.js';
|
|
import { readTextIfExists, resolveProject, toPosix, workspaceRoot } from '../core/workspace.js';
|
|
export function buildEnvPolicy(input: EnvPolicyInput): Record<string, unknown> {
|
|
const project = resolveProject(input.project);
|
|
const envFiles = ['.env.development', '.env.production', '.env.example'].map((file) => {
|
|
const absolutePath = path.join(project.path, file);
|
|
return {
|
|
exists: existsSync(absolutePath),
|
|
file,
|
|
relativePath: toPosix(path.relative(workspaceRoot, absolutePath)),
|
|
};
|
|
});
|
|
const gitignore = readTextIfExists(path.join(project.path, '.gitignore'));
|
|
const ignoredRules = gitignore
|
|
.split(/\r?\n/)
|
|
.map((line) => line.trim())
|
|
.filter(Boolean);
|
|
|
|
return {
|
|
envFiles,
|
|
ignoredRules: ignoredRules.filter((line) => line.includes('.env')),
|
|
policy: [
|
|
'真实开发和生产配置只保留在本地,不提交。',
|
|
'.env.example 用于维护变量名和示例值。',
|
|
'接口、数据库、WordPress、MinIO 等真实账号密码不得写入文档或提交记录。',
|
|
],
|
|
project,
|
|
warnings: envFiles
|
|
.filter((file) => file.file !== '.env.example' && file.exists)
|
|
.map((file) => `${file.file} 存在,提交前确认已被 .gitignore 忽略。`),
|
|
};
|
|
}
|
|
|
|
export async function scanTaskRisk(input: RiskScanInput): Promise<Record<string, unknown>> {
|
|
const project = resolveProject(input.project);
|
|
let changedFiles = input.changedFiles || [];
|
|
|
|
if (changedFiles.length === 0 && detectRepoType(project.path) === 'git') {
|
|
const status = await tryExecFile('git', ['status', '--short'], project.path);
|
|
if (status.ok) {
|
|
changedFiles = status.stdout
|
|
.split(/\r?\n/)
|
|
.map((line) => line.slice(3).trim())
|
|
.filter(Boolean);
|
|
}
|
|
}
|
|
|
|
const risks: Array<{
|
|
file: string;
|
|
level: 'high' | 'medium';
|
|
reason: string;
|
|
suggestion: string;
|
|
}> = [];
|
|
const addRisk = (
|
|
level: 'high' | 'medium',
|
|
file: string,
|
|
reason: string,
|
|
suggestion: string,
|
|
) => {
|
|
risks.push({ file, level, reason, suggestion });
|
|
};
|
|
|
|
for (const file of changedFiles) {
|
|
const normalized = file.replaceAll('\\', '/');
|
|
|
|
if (/\.env\.(development|production)$/.test(normalized)) {
|
|
addRisk('high', file, '真实环境配置文件有改动。', '确认不会提交真实密钥和密码。');
|
|
}
|
|
|
|
if (/package\.json$|pnpm-lock\.yaml$|package-lock\.json$|yarn\.lock$/.test(normalized)) {
|
|
addRisk('medium', file, '依赖或脚本发生变化。', '确认锁文件与 package.json 同步,并运行对应轻量验证。');
|
|
}
|
|
|
|
if (normalized.includes('/components/ktTable/')) {
|
|
addRisk(
|
|
'medium',
|
|
file,
|
|
'KtTable 是高频核心组件,布局和滚动性能容易回归。',
|
|
'保持原生 antdv-next 表格能力,避免监听 scroll/resize 做高频状态更新。',
|
|
);
|
|
}
|
|
|
|
if (/src\/admin\/|src\/.*controller|src\/.*service|src\/.*dto/.test(normalized)) {
|
|
addRisk('medium', file, '后端接口或业务逻辑改动。', '本地启动或复用服务后真实调用对应接口。');
|
|
}
|
|
|
|
if (/Jenkinsfile|deploy\/|k8s\/|nginx.*\.conf/.test(normalized)) {
|
|
addRisk('medium', file, '部署链路配置改动。', '只验证配置语法或生成产物,不主动触发远程部署。');
|
|
}
|
|
}
|
|
|
|
return {
|
|
changedFiles,
|
|
project,
|
|
risks,
|
|
safeDefaults: [
|
|
'不覆盖用户已有改动。',
|
|
'只运行必要验证。',
|
|
'提交前重新检查 status 和 diff。',
|
|
],
|
|
};
|
|
}
|