guanghulab/scripts/secrets-validator.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

206 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);