guanghulab/scripts/secrets-validator.js

206 lines
6.6 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
/**
*
* 🔺 Sovereign: TCS-0002 | Root: SYS-GLW-0001
* 📜 Copyright: 国作登字-2026-A-00037559
*
*
* 铸渊 · 密钥配置验证器
* scripts/secrets-validator.js
*
* 功能验证所有必需的 GitHub Secrets 是否已配置
* 用法node scripts/secrets-validator.js [--check] [--report]
*
* 指令SY-CMD-KEY-012 · 密钥全量清理与统一替换
*/
const fs = require('fs');
const path = require('path');
// ━━━ 加载密钥清单 ━━━
const manifestPath = path.join(__dirname, '..', 'brain', 'secrets-manifest.json');
let manifest;
try {
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
} catch (e) {
console.error('❌ 无法加载密钥清单: brain/secrets-manifest.json');
process.exit(1);
}
// ━━━ 从环境变量检查密钥状态 ━━━
function checkSecrets() {
const results = {
configured: [],
missing: [],
optional_configured: [],
optional_missing: []
};
// 检查必需密钥
for (const secret of manifest.secrets.required) {
const envName = secret.name;
if (process.env[envName] && process.env[envName].length > 0) {
results.configured.push(secret);
} else {
results.missing.push(secret);
}
}
// 检查可选密钥
for (const secret of manifest.secrets.optional) {
const envName = secret.name;
if (process.env[envName] && process.env[envName].length > 0) {
results.optional_configured.push(secret);
} else {
results.optional_missing.push(secret);
}
}
return results;
}
// ━━━ 扫描工作流文件验证密钥引用一致性 ━━━
function scanWorkflows() {
const workflowDir = path.join(__dirname, '..', '.github', 'workflows');
const issues = [];
if (!fs.existsSync(workflowDir)) {
return { issues: ['工作流目录不存在'], stats: {} };
}
const files = fs.readdirSync(workflowDir).filter(f => f.endsWith('.yml'));
const secretRefs = new Map();
let oldSecretRefs = [];
// 旧密钥名称列表(不应再出现)
const oldSecretNames = Object.keys(manifest.migration_mapping.mappings);
for (const file of files) {
const content = fs.readFileSync(path.join(workflowDir, file), 'utf8');
const matches = content.match(/secrets\.([A-Z_0-9]+)/g) || [];
for (const match of matches) {
const secretName = match.replace('secrets.', '');
// 跳过 GITHUB_TOKEN自动提供
if (secretName === 'GITHUB_TOKEN') continue;
if (!secretRefs.has(secretName)) {
secretRefs.set(secretName, []);
}
secretRefs.get(secretName).push(file);
// 检查是否使用了旧密钥名称
if (oldSecretNames.includes(secretName)) {
oldSecretRefs.push({ secret: secretName, file, newName: manifest.migration_mapping.mappings[secretName] });
}
}
}
if (oldSecretRefs.length > 0) {
issues.push(`⚠️ 发现 ${oldSecretRefs.length} 处旧密钥名称引用未迁移`);
}
return {
issues,
oldSecretRefs,
stats: {
total_workflows: files.length,
unique_secrets: secretRefs.size,
secret_refs: Object.fromEntries(secretRefs)
}
};
}
// ━━━ 生成报告 ━━━
function generateReport(mode) {
const envCheck = checkSecrets();
const workflowScan = scanWorkflows();
if (mode === 'check') {
// CI模式仅检查工作流引用是否已迁移到ZY_*
console.log('🔍 铸渊密钥迁移验证');
console.log('═'.repeat(50));
if (workflowScan.oldSecretRefs && workflowScan.oldSecretRefs.length > 0) {
console.log(`\n❌ 发现 ${workflowScan.oldSecretRefs.length} 处旧密钥引用:`);
for (const ref of workflowScan.oldSecretRefs) {
console.log(` ${ref.file}: ${ref.secret} → 应改为 ${ref.newName}`);
}
process.exit(1);
} else {
console.log('\n✅ 所有工作流密钥引用已迁移到 ZY_* 命名体系');
console.log(` 工作流总数: ${workflowScan.stats.total_workflows}`);
console.log(` 唯一密钥数: ${workflowScan.stats.unique_secrets}`);
}
return;
}
// 完整报告
console.log('');
console.log('🔺 铸渊密钥配置验证报告');
console.log('═'.repeat(60));
console.log(`📋 密钥清单版本: ${manifest._meta.manifest_id}`);
console.log(`📅 清单创建时间: ${manifest._meta.created}`);
console.log('');
// 必需密钥状态
console.log('━━━ 必需密钥 (Required) ━━━');
console.log(` ✅ 已配置: ${envCheck.configured.length}/${manifest.secrets.required.length}`);
console.log(` ❌ 未配置: ${envCheck.missing.length}/${manifest.secrets.required.length}`);
console.log('');
if (envCheck.missing.length > 0) {
console.log(' 未配置的必需密钥:');
for (const s of envCheck.missing) {
console.log(`${s.name}${s.description}`);
}
console.log('');
}
if (envCheck.configured.length > 0) {
console.log(' 已配置的必需密钥:');
for (const s of envCheck.configured) {
console.log(`${s.name}`);
}
console.log('');
}
// 可选密钥状态
console.log('━━━ 可选密钥 (Optional) ━━━');
console.log(` ✅ 已配置: ${envCheck.optional_configured.length}/${manifest.secrets.optional.length}`);
console.log(` ⬜ 未配置: ${envCheck.optional_missing.length}/${manifest.secrets.optional.length}`);
console.log('');
// 工作流扫描
console.log('━━━ 工作流密钥引用扫描 ━━━');
console.log(` 工作流总数: ${workflowScan.stats.total_workflows}`);
console.log(` 唯一密钥数: ${workflowScan.stats.unique_secrets}`);
if (workflowScan.issues.length > 0) {
for (const issue of workflowScan.issues) {
console.log(` ${issue}`);
}
} else {
console.log(' ✅ 所有引用已使用 ZY_* 命名');
}
console.log('');
// 总结
const allRequired = envCheck.missing.length === 0;
console.log('━━━ 总结 ━━━');
if (allRequired) {
console.log(' 🟢 所有必需密钥已配置 · 系统可以正常运行');
} else {
console.log(` 🔴 还有 ${envCheck.missing.length} 个必需密钥未配置`);
console.log(' 请参考 brain/secrets-manifest.json 逐一配置');
}
console.log('');
}
// ━━━ 主程序 ━━━
const args = process.argv.slice(2);
const mode = args.includes('--check') ? 'check' : 'report';
generateReport(mode);