From 78e97f6fee4de405f81050f7cd012acf4d506b45 Mon Sep 17 00:00:00 2001 From: deploy-bot Date: Tue, 2 Jun 2026 21:43:57 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=20spec=20=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=207=20=E4=B8=AA=E9=97=AE=E9=A2=98=EF=BC=88test=20?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E3=80=81=E5=A4=B1=E8=B4=A5=E7=9F=AD=E8=B7=AF?= =?UTF-8?q?=E3=80=81=E5=8F=98=E9=87=8F=E6=A0=A1=E9=AA=8C=E7=AD=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-02-deploy-nginx-site-enable-fix-design.md | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/docs/superpowers/specs/2026-06-02-deploy-nginx-site-enable-fix-design.md b/docs/superpowers/specs/2026-06-02-deploy-nginx-site-enable-fix-design.md index 0caab5c..8c9fc8c 100644 --- a/docs/superpowers/specs/2026-06-02-deploy-nginx-site-enable-fix-design.md +++ b/docs/superpowers/specs/2026-06-02-deploy-nginx-site-enable-fix-design.md @@ -60,13 +60,21 @@ def enable_nginx_site(remote_conf: str, domain: str) -> bool: ### 5 步骤流程 -| 步骤 | SSH 命令 | 失败处理 | -|---|---|---| -| 1. 诊断 | `ls -ld /etc/nginx/sites-enabled/{domain}.conf 2>&1; echo "---"; readlink -f /etc/nginx/sites-enabled/{domain}.conf 2>&1` | 仅诊断,不失败 | -| 2. 清理 | 若诊断结果是目录:`rm -rf `
若是文件/链/不存在:跳过(rm -f 安全) | 失败时打印 stderr + 提示"权限可能不足" | -| 3. 建链 | `ln -snf {remote_conf} /etc/nginx/sites-enabled/{domain}.conf` | 失败时提示"scp_file 是否成功" | -| 4. 清默认 | `rm -f /etc/nginx/sites-enabled/default` | 失败时提示"default 文件被占用或权限不足" | -| 5. 验证 | `readlink /etc/nginx/sites-enabled/{domain}.conf && ls -la /etc/nginx/sites-enabled/` | 失败时打印实际值与期望值 | +| 步骤 | SSH 命令 | 失败处理 | 失败时是否继续 | +|---|---|---|---| +| 1. 诊断 | `test -e && (test -L && echo "symlink" \|\| (test -d && echo "dir" \|\| echo "file")) \|\| echo "missing"` | 仅诊断,不失败 | — | +| 2. 清理 | `case 诊断结果 in symlink) unlink ;; dir) rm -rf ;; file) rm -f ;; missing) ;; esac` | 失败时打印 stderr + 提示"权限可能不足" | **否**(清理失败则终止,不重建链) | +| 3. 建链 | `ln -snf {remote_conf} /etc/nginx/sites-enabled/{domain}.conf` | 失败时提示"scp_file 是否成功" | — | +| 4. 清默认 | `rm -f /etc/nginx/sites-enabled/default` | 失败时提示"default 文件被占用或权限不足" | — | +| 5. 验证 | `readlink /etc/nginx/sites-enabled/{domain}.conf && ls -la /etc/nginx/sites-enabled/` | 失败时打印实际值与期望值 | — | + +**关键决策**: +- **步骤 2 用 `test -e/-L/-d/-f` 替代 `ls -ld` 解析**:shell 内置 test 比解析 `ls` 输出更可靠,跨 Unix 工具版本差异小 +- **步骤 2 失败则短路终止**:清理失败时建链毫无意义(脏状态仍在),立即返回 False 让用户介入 +- **有效 symlink 走 `unlink` 而非 `rm -f`**:与目录/文件区分对待,避免误删有效链接 +- **缺失(missing)跳过清理**:直接进建链步骤,是最常见的健康场景 + +**超时设置**:每步独立调用 `ssh_command(cmd, timeout=30)`,与现有 `ssh_command()` 默认值一致。**不修改** `ssh_command()` / `run_ssh_args()` 底层,也不需要新增超时参数。30s 对单步 SSH 命令(不涉及大文件传输)足够;如未来出现网络慢问题,可在调用处单独覆盖(如 `ssh_command(cmd, timeout=60)`)。 ### 集成位置 @@ -75,17 +83,43 @@ def enable_nginx_site(remote_conf: str, domain: str) -> bool: - **第 250-256 行**:替换 `ln -snf ... && rm -f ...` 块为 `enable_nginx_site(remote_conf, DOMAIN)` 调用 - **第 232-266 行**(`deploy_nginx` 函数)保持其他逻辑不变(scp_file 上传 conf、nginx -t、reload) - **新增函数** `enable_nginx_site` 放在 `deploy_nginx` 之前 +- **实施方式**:5 步都是直接调用现有 `ssh_command()`(不修改底层),只是用 Python `if/elif/else` 在函数内串起来 + +### 变量安全校验 + +`enable_nginx_site(remote_conf, domain)` 函数入口处对 `domain` 做基本校验,防止变量污染导致误操作: + +```python +import re +if not domain or not re.match(r'^[a-zA-Z0-9.-]+$', domain): + log_error(f"非法 domain 名称: {domain!r}") + return False +``` + +校验规则: +- 非空 +- 仅包含字母、数字、点、连字符 +- 不含 `/`、`*`、`;`、`&`、`|`、`$`、空格等 shell 特殊字符 + +`remote_conf` 来源固定(f-string 拼接 `f"/etc/nginx/sites-available/{DOMAIN}.conf"`),与 `domain` 一同校验。 ### 错误信息改进 -`enable_nginx_site` 内部用统一的 `log_error` 输出结构化信息: +`enable_nginx_site` 内部用统一的 `log_error` 输出结构化信息,**使用描述性名称而非"步骤 N"**: ``` -[ERROR] 启用站点失败 - 步骤 3 建链: Permission denied +[ERROR] 启用站点失败 - 建链步骤: Permission denied [ERROR] 上下文: 目标 = /etc/nginx/sites-enabled/lifescript.happylifeos.com.conf [ERROR] 提示: 确认 scp_file 是否成功上传了 sites-available 下的 conf 文件 ``` +各步骤使用的描述性名称: +- 诊断步骤 +- 清理步骤 +- 建链步骤 +- 清默认步骤 +- 验证步骤 + 替代当前的"命令执行超时 (30s)",便于快速定位卡在哪一步。 ## 范围