diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index 064bae248..eaca00df3 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -368,7 +368,7 @@ npm run check:spacetime-schema `rustfmt.toml` 固定 Edition 2024 的格式化口径。Rust 源码统一使用 `cargo fmt --all --manifest-path server-rs/Cargo.toml` 格式化,并用 `npm run check:rustfmt` 做只读校验;Codex 提交前门禁、API 生产构建和 -SpacetimeDB module 生产构建都会执行同一检查,避免不同开发机或构建节点反复产生格式差异。Web 生产构建还会执行 production-ops、ESLint、主站与后台类型检查,以及排除已下线旧玩法后的当前 Vitest;API 生产构建追加 production-ops、DDD/schema/runtime-access 和 api-server 全 target 编译检查;SpacetimeDB module 生产构建追加 production-ops、DDD/schema/runtime-access 和管理员 procedure smoke。上述门禁由 `npm run check:production-ops` 反查,不能只保留在本地说明中。 +SpacetimeDB module 生产构建都会执行同一检查,避免不同开发机或构建节点反复产生格式差异。Web 生产构建还会执行 production-ops、ESLint、主站与后台类型检查,以及排除已下线旧玩法后的当前 Vitest;API 生产构建追加 production-ops、DDD/schema/runtime-access 和 api-server 全 target 编译检查;SpacetimeDB module 生产构建追加 production-ops、DDD/schema/runtime-access 和管理员 procedure smoke。上述门禁由 `npm run check:production-ops` 反查,不能只保留在本地说明中。对需要跨格式保持稳定的脚本片段,门禁按去除空白后的源码片段匹配,避免仅因换行或格式化差异误报。 ## 前端改动验收 diff --git a/scripts/check-production-ops-guardrails.mjs b/scripts/check-production-ops-guardrails.mjs index bacd4b949..15fa3b1ad 100644 --- a/scripts/check-production-ops-guardrails.mjs +++ b/scripts/check-production-ops-guardrails.mjs @@ -973,12 +973,14 @@ const checks = [ { file: 'scripts/database-backup-to-oss.mjs', includes: 'assertSufficientWorkDirSpace({dataDir, workDir, args, env})', + normalizeWhitespace: true, reason: '生产冷备份必须先做工作目录剩余空间预检,避免停库后写满磁盘。', }, { file: 'scripts/database-backup-to-oss.mjs', includes: 'restoreServicesAfterBackup({stopService, serviceStopped, restartServicesAfter, stopMarkerPath})', + normalizeWhitespace: true, reason: '生产冷备份打包失败时也必须恢复 SpacetimeDB 及依赖服务。', }, { @@ -7504,9 +7506,23 @@ const databaseTargetSourceStashes = [ let failed = false; +function includesGuardrail(content, check) { + if (!check.includes) { + return true; + } + if (!check.normalizeWhitespace) { + return content.includes(check.includes); + } + const normalizeSourceFragment = (value) => + value.replace(/\s+/gu, '').replace(/,([})])/gu, '$1'); + const normalizedContent = normalizeSourceFragment(content); + const normalizedExpected = normalizeSourceFragment(check.includes); + return normalizedContent.includes(normalizedExpected); +} + for (const check of checks) { const content = readFileSync(check.file, 'utf8'); - if (check.includes && !content.includes(check.includes)) { + if (!includesGuardrail(content, check)) { failed = true; console.error( `[check:production-ops] ${check.file} 缺少 ${check.includes}。${check.reason}`,