This commit is contained in:
@@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
- **示例页面新增独立升级验证区块**:页面增加「本次升级验证」高亮区块,显示 `v1.3.7`,验证标识更新为 `v1.3.7-example-page`,用于直观确认升级后的前端资源已切换
|
- **示例页面新增独立升级验证区块**:页面增加「本次升级验证」高亮区块,显示 `v1.3.7`,验证标识更新为 `v1.3.7-example-page`,用于直观确认升级后的前端资源已切换
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
|
||||||
|
- **复用原 Compose 项目名重建容器**:updater 在通过 helper 容器执行 `docker compose` 时,先从现有 `fonu` 容器读取 `com.docker.compose.project` 标签,并显式传入同一个项目名,避免 Compose 将现有容器视为另一个项目后重新创建网络、数据卷并撞上 `/fonu` 容器名冲突。现有容器不是 Compose 管理时,updater 会拒绝升级并提示按 Compose 方式重新部署,避免误建空数据卷
|
||||||
|
|
||||||
### 变更
|
### 变更
|
||||||
|
|
||||||
- 版本号提升到 1.3.7;`web/package.json` 与 `web/package-lock.json` 同步对齐
|
- 版本号提升到 1.3.7;`web/package.json` 与 `web/package-lock.json` 同步对齐
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func main() {
|
|||||||
Branch: envOr("FONU_UPDATE_BRANCH", "main"),
|
Branch: envOr("FONU_UPDATE_BRANCH", "main"),
|
||||||
AppURL: envOr("FONU_UPDATE_APP_URL", "http://fonu:6893"),
|
AppURL: envOr("FONU_UPDATE_APP_URL", "http://fonu:6893"),
|
||||||
HostProjectDir: envOr("FONU_UPDATE_HOST_DIR", ""),
|
HostProjectDir: envOr("FONU_UPDATE_HOST_DIR", ""),
|
||||||
|
ProjectName: envOr("FONU_UPDATE_PROJECT_NAME", ""),
|
||||||
SelfImage: envOr("FONU_UPDATE_SELF_IMAGE", ""),
|
SelfImage: envOr("FONU_UPDATE_SELF_IMAGE", ""),
|
||||||
DockerSocket: envOr("FONU_UPDATE_DOCKER_SOCK", "/var/run/docker.sock"),
|
DockerSocket: envOr("FONU_UPDATE_DOCKER_SOCK", "/var/run/docker.sock"),
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ services:
|
|||||||
FONU_UPDATE_BRANCH: main
|
FONU_UPDATE_BRANCH: main
|
||||||
FONU_UPDATE_APP_URL: http://fonu:6893
|
FONU_UPDATE_APP_URL: http://fonu:6893
|
||||||
FONU_UPDATE_HOST_DIR: ${FONU_UPDATE_HOST_DIR:-}
|
FONU_UPDATE_HOST_DIR: ${FONU_UPDATE_HOST_DIR:-}
|
||||||
|
FONU_UPDATE_PROJECT_NAME: ${FONU_UPDATE_PROJECT_NAME:-fonu}
|
||||||
FONU_UPDATE_SELF_IMAGE: fonu-updater:${FONU_VERSION:-local}
|
FONU_UPDATE_SELF_IMAGE: fonu-updater:${FONU_VERSION:-local}
|
||||||
FONU_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
|
FONU_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
|
||||||
TZ: ${TZ:-Asia/Shanghai}
|
TZ: ${TZ:-Asia/Shanghai}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ services:
|
|||||||
FONU_UPDATE_MODE: local
|
FONU_UPDATE_MODE: local
|
||||||
FONU_UPDATE_APP_URL: http://fonu:6893
|
FONU_UPDATE_APP_URL: http://fonu:6893
|
||||||
FONU_UPDATE_HOST_DIR: ${FONU_UPDATE_HOST_DIR:-}
|
FONU_UPDATE_HOST_DIR: ${FONU_UPDATE_HOST_DIR:-}
|
||||||
|
FONU_UPDATE_PROJECT_NAME: ${FONU_UPDATE_PROJECT_NAME:-fonu}
|
||||||
FONU_UPDATE_SELF_IMAGE: fonu-updater:${FONU_VERSION:-local}
|
FONU_UPDATE_SELF_IMAGE: fonu-updater:${FONU_VERSION:-local}
|
||||||
FONU_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
|
FONU_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
|
||||||
TZ: ${TZ:-Asia/Shanghai}
|
TZ: ${TZ:-Asia/Shanghai}
|
||||||
|
|||||||
@@ -182,6 +182,12 @@ docker compose -f /实际/的/Compose/目录/docker-compose.hub.yml up -d --buil
|
|||||||
|
|
||||||
状态接口会明确提示 Compose 文件缺失,修复挂载或设置覆盖后按钮才会重新显示。
|
状态接口会明确提示 Compose 文件缺失,修复挂载或设置覆盖后按钮才会重新显示。
|
||||||
|
|
||||||
|
### 报 `container name "/fonu" is already in use`
|
||||||
|
|
||||||
|
这表示 Compose 没有识别到当前容器所属的原项目,可能把升级当成新项目并尝试新建网络与数据卷。v1.3.7 的更新版 updater 会读取现有容器的 `com.docker.compose.project` 标签,并显式复用原项目名。
|
||||||
|
|
||||||
|
如果现有 `fonu` 容器不是由 Compose 管理,updater 会拒绝升级,避免新建 `fonu_fonu-data` 等空数据卷。应先按 Compose 方式重新部署,再使用一键升级。
|
||||||
|
|
||||||
### `docker-compose.hub.yml` 模式无法读取远端版本
|
### `docker-compose.hub.yml` 模式无法读取远端版本
|
||||||
|
|
||||||
检查 sidecar 容器内是否可以访问 Git 仓库:
|
检查 sidecar 容器内是否可以访问 Git 仓库:
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ func (s *Server) compose(ctx context.Context, version string, args ...string) (s
|
|||||||
if plan.helper {
|
if plan.helper {
|
||||||
return s.composeViaHelper(ctx, plan, version, args...)
|
return s.composeViaHelper(ctx, plan, version, args...)
|
||||||
}
|
}
|
||||||
fullArgs := append([]string{"compose", "-f", plan.containerCompose}, args...)
|
fullArgs := append([]string{"compose", "--project-name", plan.projectName, "-f", plan.containerCompose}, args...)
|
||||||
env := []string{"FONU_VERSION=" + version}
|
env := []string{"FONU_VERSION=" + version}
|
||||||
return runCommand(ctx, s.cfg.ProjectDir, env, "docker", fullArgs...)
|
return runCommand(ctx, s.cfg.ProjectDir, env, "docker", fullArgs...)
|
||||||
}
|
}
|
||||||
@@ -340,7 +340,7 @@ func (s *Server) composeViaHelper(ctx context.Context, plan composePlan, version
|
|||||||
"-w", "/workspace",
|
"-w", "/workspace",
|
||||||
"-e", "FONU_VERSION=" + version,
|
"-e", "FONU_VERSION=" + version,
|
||||||
image,
|
image,
|
||||||
"compose", "-f", plan.helperComposePath,
|
"compose", "--project-name", plan.projectName, "-f", plan.helperComposePath,
|
||||||
}
|
}
|
||||||
helperArgs = append(helperArgs, args...)
|
helperArgs = append(helperArgs, args...)
|
||||||
return runCommand(ctx, "", nil, "docker", helperArgs...)
|
return runCommand(ctx, "", nil, "docker", helperArgs...)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type Config struct {
|
|||||||
Branch string
|
Branch string
|
||||||
AppURL string
|
AppURL string
|
||||||
HostProjectDir string
|
HostProjectDir string
|
||||||
|
ProjectName string
|
||||||
SelfImage string
|
SelfImage string
|
||||||
DockerSocket string
|
DockerSocket string
|
||||||
BuildTimeout time.Duration
|
BuildTimeout time.Duration
|
||||||
@@ -49,6 +50,7 @@ type Server struct {
|
|||||||
|
|
||||||
type composePlan struct {
|
type composePlan struct {
|
||||||
helper bool
|
helper bool
|
||||||
|
projectName string
|
||||||
containerCompose string
|
containerCompose string
|
||||||
hostProjectDir string
|
hostProjectDir string
|
||||||
hostComposePath string
|
hostComposePath string
|
||||||
@@ -83,6 +85,7 @@ func NewServer(cfg Config, logger *slog.Logger) (*Server, error) {
|
|||||||
cfg.DockerSocket = "/var/run/docker.sock"
|
cfg.DockerSocket = "/var/run/docker.sock"
|
||||||
}
|
}
|
||||||
cfg.HostProjectDir = strings.TrimSpace(cfg.HostProjectDir)
|
cfg.HostProjectDir = strings.TrimSpace(cfg.HostProjectDir)
|
||||||
|
cfg.ProjectName = strings.TrimSpace(cfg.ProjectName)
|
||||||
cfg.SelfImage = strings.TrimSpace(cfg.SelfImage)
|
cfg.SelfImage = strings.TrimSpace(cfg.SelfImage)
|
||||||
if cfg.BuildTimeout <= 0 {
|
if cfg.BuildTimeout <= 0 {
|
||||||
cfg.BuildTimeout = 30 * time.Minute
|
cfg.BuildTimeout = 30 * time.Minute
|
||||||
@@ -184,9 +187,13 @@ func (s *Server) ensureComposeFile(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) resolveComposePlan(ctx context.Context) (composePlan, error) {
|
func (s *Server) resolveComposePlan(ctx context.Context) (composePlan, error) {
|
||||||
|
projectName, err := s.composeProjectName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return composePlan{}, err
|
||||||
|
}
|
||||||
info, err := os.Stat(s.composePath)
|
info, err := os.Stat(s.composePath)
|
||||||
if err == nil && !info.IsDir() {
|
if err == nil && !info.IsDir() {
|
||||||
return composePlan{containerCompose: s.composePath}, nil
|
return composePlan{projectName: projectName, containerCompose: s.composePath}, nil
|
||||||
}
|
}
|
||||||
if err == nil && info.IsDir() {
|
if err == nil && info.IsDir() {
|
||||||
return composePlan{}, fmt.Errorf("Compose 路径是目录而不是文件:%s", s.composePath)
|
return composePlan{}, fmt.Errorf("Compose 路径是目录而不是文件:%s", s.composePath)
|
||||||
@@ -197,6 +204,7 @@ func (s *Server) resolveComposePlan(ctx context.Context) (composePlan, error) {
|
|||||||
if pathErr == nil {
|
if pathErr == nil {
|
||||||
return composePlan{
|
return composePlan{
|
||||||
helper: true,
|
helper: true,
|
||||||
|
projectName: projectName,
|
||||||
hostProjectDir: hostDir,
|
hostProjectDir: hostDir,
|
||||||
hostComposePath: hostComposePath,
|
hostComposePath: hostComposePath,
|
||||||
helperComposePath: helperPath,
|
helperComposePath: helperPath,
|
||||||
@@ -206,6 +214,21 @@ func (s *Server) resolveComposePlan(ctx context.Context) (composePlan, error) {
|
|||||||
return composePlan{}, fmt.Errorf("找不到 Compose 文件 %s,且无法自动定位宿主机目录;请设置 FONU_UPDATE_HOST_DIR", s.composePath)
|
return composePlan{}, fmt.Errorf("找不到 Compose 文件 %s,且无法自动定位宿主机目录;请设置 FONU_UPDATE_HOST_DIR", s.composePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) composeProjectName(ctx context.Context) (string, error) {
|
||||||
|
projectName, err := s.dockerLabel(ctx, "com.docker.compose.project")
|
||||||
|
projectName = strings.TrimSpace(projectName)
|
||||||
|
if projectName != "" {
|
||||||
|
return projectName, nil
|
||||||
|
}
|
||||||
|
if s.cfg.ProjectName != "" {
|
||||||
|
return s.cfg.ProjectName, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("无法读取现有容器的 Compose 项目标签:%w", err)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("现有 fonu 容器不是由 Compose 管理,无法安全一键升级;请按 Compose 方式重新部署")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) discoverHostCompose(ctx context.Context) (string, string, error) {
|
func (s *Server) discoverHostCompose(ctx context.Context) (string, string, error) {
|
||||||
if s.cfg.HostProjectDir != "" {
|
if s.cfg.HostProjectDir != "" {
|
||||||
if !filepath.IsAbs(s.cfg.HostProjectDir) {
|
if !filepath.IsAbs(s.cfg.HostProjectDir) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func TestNewServerDefaultsAndValidation(t *testing.T) {
|
|||||||
|
|
||||||
func TestEnsureComposeFile(t *testing.T) {
|
func TestEnsureComposeFile(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
server, err := NewServer(Config{ProjectDir: dir, ComposeFile: "docker-compose.yml"}, nil)
|
server, err := NewServer(Config{ProjectDir: dir, ComposeFile: "docker-compose.yml", ProjectName: "fonu"}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -69,6 +69,7 @@ func TestResolveComposePlanUsesHostProjectDir(t *testing.T) {
|
|||||||
ProjectDir: containerDir,
|
ProjectDir: containerDir,
|
||||||
ComposeFile: "docker-compose.hub.yml",
|
ComposeFile: "docker-compose.hub.yml",
|
||||||
HostProjectDir: hostDir,
|
HostProjectDir: hostDir,
|
||||||
|
ProjectName: "fonu",
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user