#!/usr/bin/env node import fs from 'node:fs'; const scenario = String(process.argv[2] ?? '').trim().toUpperCase(); const source = fs.readFileSync( new URL('./release-portal-runtime-prod.sh', import.meta.url), 'utf8', ); function requirePatterns(patterns) { const missing = patterns .filter(({ pattern }) => !pattern.test(source)) .map(({ label }) => label); if (missing.length) { console.error(`FAIL ${scenario} missing=${missing.join(',')}`); process.exit(1); } console.log(`PASS ${scenario} ${patterns.map(({ label }) => label).join(',')}`); } if (scenario === 'REL-08') { requirePatterns([ { label: 'nine_goosed', pattern: /containers\[@\].*!= 9/ }, { label: 'ports_18006_18014', pattern: /seq 18006 18014/ }, { label: 'force_recreate', pattern: /up -d --force-recreate/ }, { label: 'mcp_identity', pattern: /\/opt\/portal\/mindspace-sandbox-mcp\.mjs/ }, { label: 'tunnel_health', pattern: /ensure_portal_tunnel/ }, ]); } else if (scenario === 'REL-09') { requirePatterns([ { label: 'error_trap', pattern: /trap 'rollback' ERR/ }, { label: 'failed_new_live_archived_before_old_live_restore', pattern: /mv "\$\{APP_DIR\}" "\$\{FAILED_LIVE_DIR\}"[\s\S]*mv "\$\{OLD_LIVE_DIR\}" "\$\{APP_DIR\}"/, }, { label: 'old_portal_restart', pattern: /run-memind-portal-prod\.sh/ }, ]); } else if (scenario === 'REL-10') { requirePatterns([ { label: 'candidate_checksum', pattern: /shasum -a 256 -c/ }, { label: 'full_backup', pattern: /备份当前 live 全目录/ }, { label: 'persistent_backup', pattern: /单独备份持久目录/ }, { label: 'reject_new_runs', pattern: /block_new_agent_runs_for_release/ }, { label: 'active_run_drain', pattern: /drain_active_agent_runs/ }, { label: 'pre_swap_recheck', pattern: /verify_no_active_agent_runs_before_swap/ }, ]); } else { console.error('usage: node scripts/check-release-runtime-safety.mjs REL-08|REL-09|REL-10'); process.exit(2); }