diff --git a/CHANGELOG.md b/CHANGELOG.md index 408854e..4824d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### 修复 - **限流状态损坏时拒绝覆盖**:`readLimitState` 原来忽略 JSON 解析错误并返回空 map,保存任一规则时可能把其他域名的限流配置清空。现在文件不存在仍视为空状态,读取失败、空文件或 JSON 损坏则返回错误,`applyLimitState` / `clearLimitState` 会保留原文件与现有 Nginx zone,不再覆盖 +- **一键升级宿主机目录自动发现**:当 Compose 标签里的 `working_dir` 是 `/workspace` 这类容器路径时,不再直接把它当宿主机目录;优先使用 `project.config_files` 推导真实目录,无法得到宿主机路径时明确报错,避免 helper 挂载 `/workspace:/workspace` 后找不到 Compose 文件 ### 变更 diff --git a/docs/system-upgrade.md b/docs/system-upgrade.md index 694d67d..8e2ecce 100644 --- a/docs/system-upgrade.md +++ b/docs/system-upgrade.md @@ -188,6 +188,10 @@ docker compose -f /实际/的/Compose/目录/docker-compose.hub.yml up -d --buil 如果现有 `fonu` 容器不是由 Compose 管理,updater 会拒绝升级,避免新建 `fonu_fonu-data` 等空数据卷。应先按 Compose 方式重新部署,再使用一键升级。 +### helper 命令里出现 `-v /workspace:/workspace` + +说明 Compose 标签只返回了容器内路径。更新后的 updater 会优先从 `project.config_files` 标签推导宿主机目录,并忽略 `/workspace` 这类容器路径。若标签仍无法提供真实宿主机路径,应检查部署工具的 Compose 标签是否完整;正常情况下无需手工设置目录。 + ### `docker-compose.hub.yml` 模式无法读取远端版本 检查 sidecar 容器内是否可以访问 Git 仓库: diff --git a/internal/updater/server.go b/internal/updater/server.go index 514a53f..b3957d7 100644 --- a/internal/updater/server.go +++ b/internal/updater/server.go @@ -230,7 +230,7 @@ func (s *Server) composeProjectName(ctx context.Context) (string, error) { } func (s *Server) discoverHostCompose(ctx context.Context) (string, string, error) { - if s.cfg.HostProjectDir != "" { + if s.cfg.HostProjectDir != "" && !isContainerPath(s.cfg.HostProjectDir, s.cfg.ProjectDir) { if !filepath.IsAbs(s.cfg.HostProjectDir) { return "", "", fmt.Errorf("FONU_UPDATE_HOST_DIR 必须是绝对路径") } @@ -241,17 +241,32 @@ func (s *Server) discoverHostCompose(ctx context.Context) (string, string, error hostDir := strings.TrimSpace(workingDir) hostCompose := chooseComposeFile(configFiles, filepath.Base(s.cfg.ComposeFile)) if hostCompose == "" { - return "", "", fmt.Errorf("无法从容器标签读取 Compose 文件") + if hostDir == "" || isContainerPath(hostDir, s.cfg.ProjectDir) { + return "", "", fmt.Errorf("无法从容器标签读取宿主机 Compose 目录") + } + hostCompose = filepath.Join(hostDir, filepath.Base(s.cfg.ComposeFile)) } - if hostDir == "" { + if hostDir == "" || isContainerPath(hostDir, s.cfg.ProjectDir) { hostDir = filepath.Dir(hostCompose) } if !filepath.IsAbs(hostDir) || !filepath.IsAbs(hostCompose) { return "", "", fmt.Errorf("容器标签中的 Compose 路径不是绝对路径") } + if isContainerPath(hostDir, s.cfg.ProjectDir) { + return "", "", fmt.Errorf("容器标签只提供了容器内路径 %s,无法定位宿主机 Compose 目录", hostDir) + } return filepath.Clean(hostDir), filepath.Clean(hostCompose), nil } +func isContainerPath(path, containerDir string) bool { + path = filepath.Clean(strings.TrimSpace(path)) + containerDir = filepath.Clean(strings.TrimSpace(containerDir)) + if path == "" || containerDir == "" || path == "." { + return false + } + return path == containerDir || strings.HasPrefix(path, containerDir+string(filepath.Separator)) +} + func (s *Server) dockerLabel(ctx context.Context, label string) (string, error) { format := fmt.Sprintf(`{{ index .Config.Labels %q }}`, label) checkCtx, cancel := context.WithTimeout(ctx, 5*time.Second) diff --git a/internal/updater/server_test.go b/internal/updater/server_test.go index c7b7c4a..cd0cae2 100644 --- a/internal/updater/server_test.go +++ b/internal/updater/server_test.go @@ -85,3 +85,20 @@ func TestResolveComposePlanUsesHostProjectDir(t *testing.T) { t.Fatalf("helper compose 路径不正确: %s", plan.helperComposePath) } } + +func TestIsContainerPath(t *testing.T) { + cases := []struct { + path string + want bool + }{ + {"/workspace", true}, + {"/workspace/docker-compose.yml", true}, + {"/vol1/1000/Docker/mafrp", false}, + {"/vol1/1000/Docker/mafrp/docker-compose.yml", false}, + } + for _, tc := range cases { + if got := isContainerPath(tc.path, "/workspace"); got != tc.want { + t.Fatalf("isContainerPath(%q) = %v, want %v", tc.path, got, tc.want) + } + } +}