feat(config): Phase 5 — 配置 Diff 验证,检测配置漂移 #1632

Merged
auto-approve-bot merged 1 commits from feat/config-phase5-diff into develop 2026-09-02 18:15:35 +08:00
Owner

Phase 5: 配置 Diff 验证

变更内容

1. 新增 scripts/config_diff_check.sh

  • 对比渲染的 .env.rendered 与服务器当前 .env
  • 输出三类变更:ADDED / REMOVED / CHANGED
  • 敏感值(KEY/SECRET/PASSWORD/TOKEN/URL)脱敏:只显示前4字符+***
  • 支持首次部署(服务器 .env 不存在 → 全部标记 ADDED)
  • 始终返回退出码 0(仅告警,不阻塞部署)
  • 末尾汇总:新增 X 项 | 删除 Y 项 | 变更 Z 项 | 无变化 N 项

2. 修改 .gitea/workflows/ci-pipeline.yml deploy-staging job

  • 在 SSH 验证后、备份上传前插入 Config Diff Check 步骤
  • SCP 下载服务器当前 .env → 运行 diff 脚本 → 清理临时文件

3. .gitignore 添加 .env.current

安全要求

  • 敏感值脱敏,CI 日志不会泄露完整密钥
  • diff 不阻塞部署(退出码始终为 0)
  • 临时文件用完即删
## Phase 5: 配置 Diff 验证 ### 变更内容 **1. 新增 `scripts/config_diff_check.sh`** - 对比渲染的 `.env.rendered` 与服务器当前 `.env` - 输出三类变更:ADDED / REMOVED / CHANGED - 敏感值(KEY/SECRET/PASSWORD/TOKEN/URL)脱敏:只显示前4字符+*** - 支持首次部署(服务器 .env 不存在 → 全部标记 ADDED) - 始终返回退出码 0(仅告警,不阻塞部署) - 末尾汇总:新增 X 项 | 删除 Y 项 | 变更 Z 项 | 无变化 N 项 **2. 修改 `.gitea/workflows/ci-pipeline.yml` deploy-staging job** - 在 SSH 验证后、备份上传前插入 Config Diff Check 步骤 - SCP 下载服务器当前 .env → 运行 diff 脚本 → 清理临时文件 **3. `.gitignore` 添加 `.env.current`** ### 安全要求 - ✅ 敏感值脱敏,CI 日志不会泄露完整密钥 - ✅ diff 不阻塞部署(退出码始终为 0) - ✅ 临时文件用完即删
xiaoxia added 1 commit 2026-09-02 18:00:39 +08:00
feat(config): Phase 5 — 配置 Diff 验证,检测配置漂移
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m2s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m55s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 5m3s
AI Code Review / AI Code Review (pull_request) Failing after 5m13s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 5m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m41s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 6m6s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m35s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 7m23s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m20s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 14m1s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 0s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 4m36s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 5m2s
9fa8a42fc2
- 创建 scripts/config_diff_check.sh:对比渲染 .env 与服务器当前 .env
  - 输出 ADDED/REMOVED/CHANGED 三类变更
  - 敏感值(KEY/SECRET/PASSWORD/TOKEN/URL)脱敏显示(前4字符+***)
  - 支持首次部署(服务器 .env 不存在时全部标记为 ADDED)
  - 始终返回退出码 0(仅告警,不阻塞部署)
  - 末尾输出汇总统计
- 修改 ci-pipeline.yml deploy-staging:SSH 验证后、上传前插入 diff 步骤
  - SCP 下载服务器当前 .env 到 CI runner
  - 运行 diff 脚本后清理临时文件
- .gitignore 添加 .env.current
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:是
  • 阻塞级问题数量:1 个

📊 审查概览

  • 整体评价:需修改
  • 建议级问题数量:1 个

🔴 阻塞级问题(必须修复)

  1. [.gitea/workflows/ci-pipeline.yml: 1271] SCP 错误处理过于宽泛导致误判
    • 问题类型:逻辑bug / 数据损坏风险
    • 问题描述:scp ... 2>/dev/null || touch .env.current 这行代码使用 || 捕获了所有失败情况。当 scp 失败时,原因可能有很多种(如网络抖动、SSH权限拒绝、密钥错误、磁盘满等),而不仅仅是“文件不存在”。该脚本会盲目地创建一个空文件,导致后续的 diff 脚本误以为服务器上没有任何配置,从而报告所有配置均为“新增”,掩盖了实际的网络或权限故障。
    • 修改建议:应先检查文件是否存在,再决定是否下载。建议修改为:
      # 先检查文件是否存在
      if ssh -p "$staging_port" -i "$key_path" -o StrictHostKeyChecking=no "${staging_user}@${staging_host}" "test -f /var/lib/xiaoxia-saas-staging/.env"; then
        scp -P "$staging_port" -i "$key_path" -o StrictHostKeyChecking=no \
          "${staging_user}@${staging_host}:/var/lib/xiaoxia-saas-staging/.env" .env.current
      else
        touch .env.current
      fi
      

💡 改进建议(不阻塞合并)

  1. [scripts/config_diff_check.sh: 107, 117, 133] Grep 正则匹配存在键名注入风险
    • 具体内容:脚本中使用 grep "^${key}=" 来查找配置项。如果 .env 文件中的键名包含正则表达式元字符(如 .*[),会导致匹配错误。例如键名为 DB.HOST,正则 ^DB.HOST= 会错误匹配 DBXHOST=。建议使用 awk 进行精确字段匹配,或者对 $key 进行正则转义。
    • 修改建议:将 grep -m1 "^${key}=" "$tmp_current" 替换为 awk -F= '$1 == "'"$key"'" {print; exit}' "$tmp_current",这样可以确保精确匹配键名。

良好实践

  • 脚本使用了 set -u 来检测未定义变量,提高了脚本的健壮性。
  • 使用 trap 确保临时文件被清理,防止残留垃圾文件。
  • 实现了敏感信息脱敏功能,避免在 CI 日志中泄露密码等敏感信息。
  • .gitignore 及时更新,防止将下载的 .env.current 敏感文件提交到仓库。

格式检查通过 | 逻辑审查需修改 | 性能良好


🤖 由 AI 代码审查机器人自动生成 | 2026-09-02 10:05:53 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:1 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:1 个 ### 🔴 阻塞级问题(必须修复) 1. **[.gitea/workflows/ci-pipeline.yml: 1271] SCP 错误处理过于宽泛导致误判** - 问题类型:逻辑bug / 数据损坏风险 - 问题描述:`scp ... 2>/dev/null || touch .env.current` 这行代码使用 `||` 捕获了所有失败情况。当 `scp` 失败时,原因可能有很多种(如网络抖动、SSH权限拒绝、密钥错误、磁盘满等),而不仅仅是“文件不存在”。该脚本会盲目地创建一个空文件,导致后续的 diff 脚本误以为服务器上没有任何配置,从而报告所有配置均为“新增”,掩盖了实际的网络或权限故障。 - 修改建议:应先检查文件是否存在,再决定是否下载。建议修改为: ```bash # 先检查文件是否存在 if ssh -p "$staging_port" -i "$key_path" -o StrictHostKeyChecking=no "${staging_user}@${staging_host}" "test -f /var/lib/xiaoxia-saas-staging/.env"; then scp -P "$staging_port" -i "$key_path" -o StrictHostKeyChecking=no \ "${staging_user}@${staging_host}:/var/lib/xiaoxia-saas-staging/.env" .env.current else touch .env.current fi ``` ### 💡 改进建议(不阻塞合并) 1. **[scripts/config_diff_check.sh: 107, 117, 133] Grep 正则匹配存在键名注入风险** - 具体内容:脚本中使用 `grep "^${key}="` 来查找配置项。如果 `.env` 文件中的键名包含正则表达式元字符(如 `.`、`*`、`[`),会导致匹配错误。例如键名为 `DB.HOST`,正则 `^DB.HOST=` 会错误匹配 `DBXHOST=`。建议使用 `awk` 进行精确字段匹配,或者对 `$key` 进行正则转义。 - 修改建议:将 `grep -m1 "^${key}=" "$tmp_current"` 替换为 `awk -F= '$1 == "'"$key"'" {print; exit}' "$tmp_current"`,这样可以确保精确匹配键名。 ### ✅ 良好实践 - 脚本使用了 `set -u` 来检测未定义变量,提高了脚本的健壮性。 - 使用 `trap` 确保临时文件被清理,防止残留垃圾文件。 - 实现了敏感信息脱敏功能,避免在 CI 日志中泄露密码等敏感信息。 - `.gitignore` 及时更新,防止将下载的 `.env.current` 敏感文件提交到仓库。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ✅ 性能良好 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-02 10:05:53 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->

🚀 预览环境已部署

项目 详情
PR号 #1632
预览链接 https://pr-1632.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1632 | | 预览链接 | [https://pr-1632.preview.xiaoxiajianji.com](https://pr-1632.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot approved these changes 2026-09-02 18:15:04 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-09-02 18:15:04 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot merged commit e6c44c9bf8 into develop 2026-09-02 18:15:35 +08:00
auto-approve-bot deleted branch feat/config-phase5-diff 2026-09-02 18:15:35 +08:00

🗑️ 预览环境已清理

PR #1632 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1632 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.