# 编码 / 换行审查 — Ubuntu 工作区（阶段 1）

| 项 | 内容 |
|----|------|
| 日期 | 2026-06-04 |
| 范围 | Git 索引 vs 工作区、运行时 UTF-8/EOL、Windows 遗留路径 |
| 前置 | `docs/audit/2026-06-02-windows-ubuntu-encoding-deep-dive.md` |

---

## 阶段 1 结论（摘要）

| 维度 | Git 索引（部署真相） | 本机工作区（tar/检出） |
|------|----------------------|----------------------|
| `check_text_eol.py` | **PASS**（206 路径无 CR） | — |
| `check_shell_eol.sh` | **PASS** | — |
| `check_worktree_eol.py` | — | **FAIL**（约 171 个跟踪文件磁盘含 `\r`） |
| 运行时写 `.env` | `install.py` / `sync_mysql_mcp_env` 已用 `text_io` | — |
| 新脚本 `proxy-env.sh` 等 | 未入库前曾 **CRLF** | 已 `sed` 去 CR |

**核心区分**：Ubuntu 生产以 **`git pull` 的索引 blob** 为准；离线 tar 解压到本机后，工作区可能是 CRLF，会导致 `source *.sh` 报 `未找到命令`（行尾 `\r`）。

---

## 1. 已有防护（保持）

| 组件 | 路径 |
|------|------|
| SSOT | `server/utils/text_io.py` |
| 索引门控 | `scripts/check_text_eol.py` |
| Shell 门控 | `deploy/check_shell_eol.sh` |
| 编辑器 | `.editorconfig` → `charset=utf-8`, `eol=lf` |
| Git | `.gitattributes` → `*.py/sh/vue/ts` = `eol=lf` |
| 部署 Pre-flight | `deploy/pre_deploy_check.sh` 调用上述门控 |

---

## 2. 本轮发现（按严重度）

### P1 — 工作区 CRLF vs 索引 LF

- **现象**：`grep -l $'\r'` 在 `server/**/*.py` 等约 **171** 个已跟踪文件命中；`file scripts/proxy-env.sh` 显示 `CRLF line terminators`。
- **影响**：bash `source scripts/proxy-env.sh` 出现 `：未找到命令`；不影响已从 Git 拉取的 Ubuntu 服务器（索引为 LF）。
- **修复**：
  ```bash
  git config core.autocrlf false
  bash scripts/normalize_worktree_lf.sh
  python3 scripts/check_worktree_eol.py
  ```

### P2 — `docker/sync_root_env.py` 未走 `text_io`（已修）

- **问题**：`read_text`/`write_text` 未归一化 CRLF，Windows 生成的 `docker/.env` 可能把 `\r` 带进 `NEXUS_*`。
- **修复**：改用 `read_utf8_lf` / `write_utf8_lf`。

### P2 — `linux_mcp_mysql.sh` 读 `.env` 未去 `\r`（已修）

- **问题**：`export MYSQL_PASSWORD=xxx\r` 导致 MCP 连库失败。
- **修复**：`line="${line%$'\r'}"`。

### P3 — `web/agent/agent.py` 读 `/etc/os-release` 无 encoding（已修）

- **修复**：`read_text(encoding="utf-8")`（Linux 标准 UTF-8）。

### 合理设计（非缺陷）

| 位置 | 说明 |
|------|------|
| `servers.py` CSV 导入 | `utf-8-sig` → 失败则 `gbk`（中文 Excel） |
| `sync_v2.read_file` | `detect_text_eol` + 前端 `stripCr` / `serializeContent` |
| `sync_v2.write_file` | `content.encode("utf-8")` 字节写入远程 |
| `posix_paths.py` | 拒绝远程路径中的 `\` |
| `auth_jwt` / `main` | HTTP header `latin-1`（RFC 7230） |

### 文档 / 配置中的 Windows 路径（仅文档，不影响运行）

- `docs/handoff-*.md`、`mysql-mcp-setup.md` 仍含 `C:\Users\...` — 阶段 2 可改为 `$NEXUS_ROOT` / `~/Nexus`。

---

## 3. 远程文件链（复查）

```
read-file:  SSH cat → str → detect_text_eol → JSON { content, eol }
write-file: JSON → .encode("utf-8") → SFTP/tee（不经本机换行转换）
```

经典 Mac `\r` 在 API 层标为 `eol: LF`；前端 `stripCr` 统一编辑为 LF。

---

## 4. 阶段 2（已完成 — 见 phase2 文档）

- [x] `frontend/` 工作区 LF 清零（`check_worktree_eol` OK）
- [x] `.cursor/rules/*.mdc` 归一化 + 门控扩展
- [x] `web/agent/agent.py` I/O 审查
- [x] `mysql-mcp-setup.md` Linux/WSL 路径
- [ ] `vue-tsc` 3 处类型错误（非编码）
- [ ] 历史 handoff 文档 Windows 路径（可选）

详见：`docs/audit/2026-06-04-encoding-review-ubuntu-phase2.md`

---

## 5. Closure

| H | 判定 | 依据 |
|---|------|------|
| H-index-LF | PASS | `check_text_eol` + `check_shell_eol` |
| H-worktree-CRLF | FIXED | `normalize_worktree_lf.sh` + `check_worktree_eol` OK |
| H-sync-root-env | FIXED | `docker/sync_root_env.py` → `text_io` |
| H-mcp-sh-env | FIXED | `linux_mcp_mysql.sh` strip `\r` |
| H-agent-os-release | FIXED | `encoding=utf-8` |

## DoD（阶段 1）

- [x] 跑通双门控 + 工作区扫描
- [x] 修复本轮 3 处代码
- [x] 新增 `check_worktree_eol.py` / `normalize_worktree_lf.sh`
- [x] 审计记录与 changelog

## 验证

```bash
python3 scripts/check_text_eol.py
bash deploy/check_shell_eol.sh
bash scripts/normalize_worktree_lf.sh
python3 scripts/check_worktree_eol.py
pytest tests/test_text_io.py -q
python3 docker/sync_root_env.py
```
